From 68a4507dbe3d9bba403155050e0a863842641d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 18 May 2026 13:58:36 -0400 Subject: [PATCH] update --- .github/workflows/ci.yml | 2 +- Cargo.lock | 166 +- Cargo.toml | 6 +- README.md | 4 +- crates/ely_app/Cargo.toml | 15 +- crates/ely_app/src/main.rs | 14 +- crates/ely_app/src/services/iosurface_mach.rs | 233 -- .../ely_app/src/services/iosurface_metal.rs | 306 --- crates/ely_app/src/services/mod.rs | 5 - crates/ely_app/src/services/servo_live.rs | 516 ++--- .../services/servo_live_iosurface_importer.rs | 147 -- .../ely_app/src/services/servo_live_types.rs | 185 +- .../ely_app/src/services/servo_live_wire.rs | 189 -- .../src/services/servo_sidecar_command.rs | 220 -- .../ely_app/src/shell/gpui_harness_tests.rs | 9 +- crates/ely_app/src/shell/web_surface.rs | 62 +- .../src/shell/web_surface_controller.rs | 21 +- crates/ely_app/src/shell/web_surface_frame.rs | 214 +- .../ely_app/src/shell/web_surface_runtime.rs | 329 ++- .../src/shell/web_surface_runtime_tests.rs | 20 +- crates/ely_app/src/shell/web_surface_state.rs | 15 +- crates/ely_app/src/shell/web_surface_tests.rs | 140 +- crates/ely_app/src/shell/web_surface_view.rs | 56 +- .../ely_app/src/shell/web_surface_worker.rs | 61 +- crates/ely_servo_host/Cargo.toml | 37 +- .../src/bin/ely_servo_sidecar.rs | 332 --- .../src/bin/ely_servo_sidecar/args.rs | 395 ---- .../src/bin/ely_servo_sidecar/args_tests.rs | 183 -- .../bin/ely_servo_sidecar/iosurface_mach.rs | 133 -- .../src/bin/ely_servo_sidecar/live.rs | 476 ---- .../src/bin/ely_servo_sidecar/live_output.rs | 348 --- .../bin/ely_servo_sidecar/live_protocol.rs | 300 --- .../src/bin/ely_servo_sidecar/live_session.rs | 240 -- .../src/bin/ely_servo_sidecar/perf.rs | 257 --- .../src/bin/ely_servo_sidecar/report.rs | 98 - crates/ely_servo_host/src/error.rs | 15 - .../src/hardware_rendering_context.rs | 398 ---- crates/ely_servo_host/src/host.rs | 10 - crates/ely_servo_host/src/iosurface_handle.rs | 48 - crates/ely_servo_host/src/lib.rs | 10 +- crates/ely_servo_host/src/runtime.rs | 136 +- crates/ely_servo_host/src/runtime_context.rs | 60 +- crates/ely_servo_host/src/runtime_webview.rs | 6 - .../tests/hardware_rendering_context.rs | 78 - .../ely_servo_host/tests/live_perf_bench.rs | 494 ----- .../tests/live_perf_bench/pixels.rs | 163 -- crates/ely_servo_host/tests/sidecar.rs | 215 -- .../tests/sidecar/site_cases.rs | 141 -- .../ely_servo_host/tests/sidecar/support.rs | 443 ---- crates/ely_servo_host/tests/software_host.rs | 16 +- docs/servo-embedding-architecture.md | 103 +- generated-icons/android/ic_launcher_hdpi.png | Bin 0 -> 4228 bytes generated-icons/android/ic_launcher_mdpi.png | Bin 0 -> 2453 bytes generated-icons/android/ic_launcher_xhdpi.png | Bin 0 -> 6216 bytes .../android/ic_launcher_xxhdpi.png | Bin 0 -> 10421 bytes .../android/ic_launcher_xxxhdpi.png | Bin 0 -> 15290 bytes generated-icons/android/playstore-512.png | Bin 0 -> 49433 bytes generated-icons/icon-source-1024.png | Bin 0 -> 82303 bytes generated-icons/ios/Contents.json | 80 + generated-icons/ios/icon-1024.png | Bin 0 -> 82303 bytes generated-icons/ios/icon-120.png | Bin 0 -> 8250 bytes generated-icons/ios/icon-152.png | Bin 0 -> 11178 bytes generated-icons/ios/icon-167.png | Bin 0 -> 12547 bytes generated-icons/ios/icon-180.png | Bin 0 -> 13991 bytes generated-icons/ios/icon-40.png | Bin 0 -> 1903 bytes generated-icons/ios/icon-58.png | Bin 0 -> 3204 bytes generated-icons/ios/icon-60.png | Bin 0 -> 3271 bytes generated-icons/ios/icon-76.png | Bin 0 -> 4521 bytes generated-icons/ios/icon-80.png | Bin 0 -> 4750 bytes generated-icons/ios/icon-87.png | Bin 0 -> 5334 bytes generated-icons/macos/AppIcon.icns | Bin 0 -> 411535 bytes .../macos/AppIcon.iconset/icon_128x128.png | Bin 0 -> 9185 bytes .../macos/AppIcon.iconset/icon_128x128@2x.png | Bin 0 -> 21502 bytes .../macos/AppIcon.iconset/icon_16x16.png | Bin 0 -> 595 bytes .../macos/AppIcon.iconset/icon_16x16@2x.png | Bin 0 -> 1456 bytes .../macos/AppIcon.iconset/icon_256x256.png | Bin 0 -> 21502 bytes .../macos/AppIcon.iconset/icon_256x256@2x.png | Bin 0 -> 49433 bytes .../macos/AppIcon.iconset/icon_32x32.png | Bin 0 -> 1456 bytes .../macos/AppIcon.iconset/icon_32x32@2x.png | Bin 0 -> 3608 bytes .../macos/AppIcon.iconset/icon_512x512.png | Bin 0 -> 49433 bytes .../macos/AppIcon.iconset/icon_512x512@2x.png | Bin 0 -> 82303 bytes generated-icons/web/apple-touch-icon.png | Bin 0 -> 13991 bytes generated-icons/web/favicon.ico | Bin 0 -> 617 bytes generated-icons/web/icon-128.png | Bin 0 -> 9185 bytes generated-icons/web/icon-144.png | Bin 0 -> 10421 bytes generated-icons/web/icon-152.png | Bin 0 -> 11178 bytes generated-icons/web/icon-16.png | Bin 0 -> 595 bytes generated-icons/web/icon-192.png | Bin 0 -> 15290 bytes generated-icons/web/icon-32.png | Bin 0 -> 1456 bytes generated-icons/web/icon-384.png | Bin 0 -> 36342 bytes generated-icons/web/icon-48.png | Bin 0 -> 2453 bytes generated-icons/web/icon-512.png | Bin 0 -> 49433 bytes generated-icons/web/icon-72.png | Bin 0 -> 4228 bytes generated-icons/web/icon-96.png | Bin 0 -> 6216 bytes scripts/create_macos_app_bundle.sh | 4 - scripts/generate_icon.py | 207 ++ scripts/verify_prd_site_rendering.sh | 3 - third_party/gpui/Cargo.toml | 8 - third_party/gpui/src/elements/mod.rs | 2 + .../gpui/src/elements/native_surface.rs | 109 + third_party/gpui/src/platform.rs | 250 ++- .../gpui/src/platform/linux/wayland/client.rs | 6 +- .../gpui/src/platform/linux/wayland/window.rs | 72 +- .../gpui/src/platform/linux/x11/window.rs | 68 +- third_party/gpui/src/platform/mac/window.rs | 65 +- .../gpui/src/platform/windows/window.rs | 65 + third_party/gpui/src/window.rs | 25 +- .../gpui_support/gpui_http_client/Cargo.toml | 133 ++ .../gpui_http_client/LICENSE-APACHE | 222 ++ .../gpui_http_client/src/async_body.rs | 149 ++ .../gpui_http_client/src/github.rs | 179 ++ .../gpui_http_client/src/github_download.rs | 189 ++ .../gpui_http_client/src/http_client.rs | 481 ++++ .../gpui_support/gpui_sum_tree/Cargo.toml | 66 + .../gpui_support/gpui_sum_tree/LICENSE-APACHE | 222 ++ .../gpui_support/gpui_sum_tree/src/cursor.rs | 819 +++++++ .../gpui_sum_tree/src/sum_tree.rs | 1558 +++++++++++++ .../gpui_sum_tree/src/tree_map.rs | 521 +++++ third_party/gpui_support/gpui_util/Cargo.toml | 194 ++ .../gpui_support/gpui_util/LICENSE-APACHE | 222 ++ .../gpui_support/gpui_util/src/arc_cow.rs | 141 ++ .../gpui_support/gpui_util/src/archive.rs | 253 +++ .../gpui_support/gpui_util/src/command.rs | 32 + third_party/gpui_support/gpui_util/src/fs.rs | 111 + .../gpui_support/gpui_util/src/markdown.rs | 268 +++ .../gpui_support/gpui_util/src/paths.rs | 1934 +++++++++++++++++ .../gpui_support/gpui_util/src/redact.rs | 15 + .../gpui_support/gpui_util/src/rel_path.rs | 579 +++++ .../gpui_support/gpui_util/src/schemars.rs | 55 + .../gpui_support/gpui_util/src/serde.rs | 7 + .../gpui_support/gpui_util/src/shell.rs | 406 ++++ .../gpui_support/gpui_util/src/shell_env.rs | 236 ++ .../gpui_support/gpui_util/src/size.rs | 46 + .../gpui_support/gpui_util/src/test.rs | 81 + .../gpui_util/src/test/assertions.rs | 62 + .../gpui_util/src/test/marked_text.rs | 281 +++ .../gpui_support/gpui_util/src/time.rs | 41 + .../gpui_support/gpui_util/src/util.rs | 1310 +++++++++++ .../gpui_support/zed-sum-tree/Cargo.toml | 69 + .../gpui_support/zed-sum-tree/LICENSE-APACHE | 222 ++ .../gpui_support/zed-sum-tree/src/cursor.rs | 817 +++++++ .../gpui_support/zed-sum-tree/src/sum_tree.rs | 1444 ++++++++++++ .../gpui_support/zed-sum-tree/src/tree_map.rs | 520 +++++ 143 files changed, 15715 insertions(+), 7204 deletions(-) delete mode 100644 crates/ely_app/src/services/iosurface_mach.rs delete mode 100644 crates/ely_app/src/services/iosurface_metal.rs delete mode 100644 crates/ely_app/src/services/servo_live_iosurface_importer.rs delete mode 100644 crates/ely_app/src/services/servo_live_wire.rs delete mode 100644 crates/ely_app/src/services/servo_sidecar_command.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/args.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/args_tests.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/iosurface_mach.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/live.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/live_output.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/live_protocol.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/live_session.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/perf.rs delete mode 100644 crates/ely_servo_host/src/bin/ely_servo_sidecar/report.rs delete mode 100644 crates/ely_servo_host/src/hardware_rendering_context.rs delete mode 100644 crates/ely_servo_host/src/iosurface_handle.rs delete mode 100644 crates/ely_servo_host/tests/hardware_rendering_context.rs delete mode 100644 crates/ely_servo_host/tests/live_perf_bench.rs delete mode 100644 crates/ely_servo_host/tests/live_perf_bench/pixels.rs delete mode 100644 crates/ely_servo_host/tests/sidecar.rs delete mode 100644 crates/ely_servo_host/tests/sidecar/site_cases.rs delete mode 100644 crates/ely_servo_host/tests/sidecar/support.rs create mode 100644 generated-icons/android/ic_launcher_hdpi.png create mode 100644 generated-icons/android/ic_launcher_mdpi.png create mode 100644 generated-icons/android/ic_launcher_xhdpi.png create mode 100644 generated-icons/android/ic_launcher_xxhdpi.png create mode 100644 generated-icons/android/ic_launcher_xxxhdpi.png create mode 100644 generated-icons/android/playstore-512.png create mode 100644 generated-icons/icon-source-1024.png create mode 100644 generated-icons/ios/Contents.json create mode 100644 generated-icons/ios/icon-1024.png create mode 100644 generated-icons/ios/icon-120.png create mode 100644 generated-icons/ios/icon-152.png create mode 100644 generated-icons/ios/icon-167.png create mode 100644 generated-icons/ios/icon-180.png create mode 100644 generated-icons/ios/icon-40.png create mode 100644 generated-icons/ios/icon-58.png create mode 100644 generated-icons/ios/icon-60.png create mode 100644 generated-icons/ios/icon-76.png create mode 100644 generated-icons/ios/icon-80.png create mode 100644 generated-icons/ios/icon-87.png create mode 100644 generated-icons/macos/AppIcon.icns create mode 100644 generated-icons/macos/AppIcon.iconset/icon_128x128.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_128x128@2x.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_16x16.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_16x16@2x.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_256x256.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_256x256@2x.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_32x32.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_32x32@2x.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_512x512.png create mode 100644 generated-icons/macos/AppIcon.iconset/icon_512x512@2x.png create mode 100644 generated-icons/web/apple-touch-icon.png create mode 100644 generated-icons/web/favicon.ico create mode 100644 generated-icons/web/icon-128.png create mode 100644 generated-icons/web/icon-144.png create mode 100644 generated-icons/web/icon-152.png create mode 100644 generated-icons/web/icon-16.png create mode 100644 generated-icons/web/icon-192.png create mode 100644 generated-icons/web/icon-32.png create mode 100644 generated-icons/web/icon-384.png create mode 100644 generated-icons/web/icon-48.png create mode 100644 generated-icons/web/icon-512.png create mode 100644 generated-icons/web/icon-72.png create mode 100644 generated-icons/web/icon-96.png create mode 100644 scripts/generate_icon.py create mode 100644 third_party/gpui/src/elements/native_surface.rs create mode 100644 third_party/gpui_support/gpui_http_client/Cargo.toml create mode 100644 third_party/gpui_support/gpui_http_client/LICENSE-APACHE create mode 100644 third_party/gpui_support/gpui_http_client/src/async_body.rs create mode 100644 third_party/gpui_support/gpui_http_client/src/github.rs create mode 100644 third_party/gpui_support/gpui_http_client/src/github_download.rs create mode 100644 third_party/gpui_support/gpui_http_client/src/http_client.rs create mode 100644 third_party/gpui_support/gpui_sum_tree/Cargo.toml create mode 100644 third_party/gpui_support/gpui_sum_tree/LICENSE-APACHE create mode 100644 third_party/gpui_support/gpui_sum_tree/src/cursor.rs create mode 100644 third_party/gpui_support/gpui_sum_tree/src/sum_tree.rs create mode 100644 third_party/gpui_support/gpui_sum_tree/src/tree_map.rs create mode 100644 third_party/gpui_support/gpui_util/Cargo.toml create mode 100644 third_party/gpui_support/gpui_util/LICENSE-APACHE create mode 100644 third_party/gpui_support/gpui_util/src/arc_cow.rs create mode 100644 third_party/gpui_support/gpui_util/src/archive.rs create mode 100644 third_party/gpui_support/gpui_util/src/command.rs create mode 100644 third_party/gpui_support/gpui_util/src/fs.rs create mode 100644 third_party/gpui_support/gpui_util/src/markdown.rs create mode 100644 third_party/gpui_support/gpui_util/src/paths.rs create mode 100644 third_party/gpui_support/gpui_util/src/redact.rs create mode 100644 third_party/gpui_support/gpui_util/src/rel_path.rs create mode 100644 third_party/gpui_support/gpui_util/src/schemars.rs create mode 100644 third_party/gpui_support/gpui_util/src/serde.rs create mode 100644 third_party/gpui_support/gpui_util/src/shell.rs create mode 100644 third_party/gpui_support/gpui_util/src/shell_env.rs create mode 100644 third_party/gpui_support/gpui_util/src/size.rs create mode 100644 third_party/gpui_support/gpui_util/src/test.rs create mode 100644 third_party/gpui_support/gpui_util/src/test/assertions.rs create mode 100644 third_party/gpui_support/gpui_util/src/test/marked_text.rs create mode 100644 third_party/gpui_support/gpui_util/src/time.rs create mode 100644 third_party/gpui_support/gpui_util/src/util.rs create mode 100644 third_party/gpui_support/zed-sum-tree/Cargo.toml create mode 100644 third_party/gpui_support/zed-sum-tree/LICENSE-APACHE create mode 100644 third_party/gpui_support/zed-sum-tree/src/cursor.rs create mode 100644 third_party/gpui_support/zed-sum-tree/src/sum_tree.rs create mode 100644 third_party/gpui_support/zed-sum-tree/src/tree_map.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48f47f3..0965303 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: - name: Test Servo engine host run: cargo test -p ely_servo_host --features servo-engine --test software_host - - name: Test PRD site compatibility + - name: Test embedded PRD site compatibility run: scripts/verify_prd_site_rendering.sh - name: Audit source file size diff --git a/Cargo.lock b/Cargo.lock index b2ff9b1..0ec016a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2243,24 +2243,17 @@ name = "ely_app" version = "0.1.0" dependencies = [ "ahash", - "core-foundation 0.10.0", - "core-video", "directories", "ed25519-dalek", "ely_browser_core", "ely_design_system", "ely_domain", + "ely_servo_host", "ely_sync_client", "gpui", "gpui-component", "gpui-component-assets", "image", - "io-surface", - "mach2", - "objc2", - "objc2-core-foundation", - "objc2-foundation", - "objc2-io-surface", "serde", "serde_json", "sha2", @@ -2270,7 +2263,6 @@ dependencies = [ "tracing-subscriber", "ureq", "url", - "uuid", ] [[package]] @@ -2309,16 +2301,12 @@ dependencies = [ "dpi", "ely_domain", "euclid", - "gleam", - "glow", - "image", - "log", - "mach2", - "objc2-io-surface", + "naga 26.0.0", + "raw-window-handle", + "rustls", "serde", "serde_json", "servo", - "surfman", "thiserror 2.0.18", "url", ] @@ -2498,17 +2486,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - [[package]] name = "errno" version = "0.3.14" @@ -3521,8 +3498,6 @@ dependencies = [ [[package]] name = "gpui_http_client" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23822b0a6d2c5e6a42507980a0ab3848610ea908942c8ef98187f646f690335e" dependencies = [ "anyhow", "async-compression", @@ -3593,8 +3568,6 @@ dependencies = [ [[package]] name = "gpui_sum_tree" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f3bedd573fafafa13d1200b356c588cf094fb2786e3684bb3f5ea59b549fa9" dependencies = [ "arrayvec", "log", @@ -3604,8 +3577,6 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68faea25903ae524de9af83990b9aa51bcbc8dd085929ac0aea7fd41905e05c3" dependencies = [ "anyhow", "async-fs", @@ -5288,7 +5259,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" dependencies = [ - "serde_core", "value-bag", ] @@ -6142,8 +6112,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.1", - "block2", - "libc", "objc2", "objc2-core-foundation", ] @@ -6158,7 +6126,6 @@ dependencies = [ "libc", "objc2", "objc2-core-foundation", - "objc2-foundation", ] [[package]] @@ -8052,15 +8019,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "serde_fmt" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e497af288b3b95d067a23a4f749f2861121ffcb2f6d8379310dcda040c345ed" -dependencies = [ - "serde_core", -] - [[package]] name = "serde_json" version = "1.0.149" @@ -10186,84 +10144,6 @@ dependencies = [ "x11-dl", ] -[[package]] -name = "sval" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb9318255ebd817902d7e279d8f8e39b35b1b9954decd5eb9ea0e30e5fd2b6a" - -[[package]] -name = "sval_buffer" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12571299185e653fdb0fbfe36cd7f6529d39d4e747a60b15a3f34574b7b97c61" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39526f24e997706c0de7f03fb7371f7f5638b66a504ded508e20ad173d0a3677" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "933dd3bb26965d682280fcc49400ac2a05036f4ee1e6dbd61bf8402d5a5c3a54" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0cda08f6d5c9948024a6551077557b1fdcc3880ff2f20ae839667d2ec2d87ed" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_nested" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d49d5e6c1f9fd0e53515819b03a97ca4eb1bff5c8ee097c43391c09ecfb19f" -dependencies = [ - "sval", - "sval_buffer", - "sval_ref", -] - -[[package]] -name = "sval_ref" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f876c5a78405375b4e19cbb9554407513b59c93dea12dc6a4af4e1d30899ca" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9ccd3b7f7200239a655e517dd3fd48d960b9111ad24bd6a5e055bef17607c7" -dependencies = [ - "serde_core", - "sval", - "sval_nested", -] - [[package]] name = "svg_fmt" version = "0.4.5" @@ -11061,12 +10941,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "typenum" version = "1.20.0" @@ -11381,36 +11255,6 @@ name = "value-bag" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" -dependencies = [ - "erased-serde", - "serde_core", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] [[package]] name = "vcpkg" @@ -13304,8 +13148,6 @@ dependencies = [ [[package]] name = "zed-sum-tree" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d490156d0d7311855564d6e1d6dccab992405a0c0e15e1c8ef18920c02177e35" dependencies = [ "arrayvec", "log", diff --git a/Cargo.toml b/Cargo.toml index 0a50608..4a3f3b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,12 @@ url = "2.5.4" uuid = { version = "1.12.1", features = ["v7"] } [patch.crates-io] -# Local GPUI 0.2.2 patch: macOS `surface(CVPixelBuffer)` accepts Servo's BGRA IOSurfaces. +# Local GPUI 0.2.2 patch used by the native shell while the upstream crate is pinned. gpui = { path = "third_party/gpui" } +gpui_http_client = { path = "third_party/gpui_support/gpui_http_client" } +gpui_sum_tree = { path = "third_party/gpui_support/gpui_sum_tree" } +gpui_util = { path = "third_party/gpui_support/gpui_util" } +zed-sum-tree = { path = "third_party/gpui_support/zed-sum-tree" } [workspace.lints.rust] unsafe_code = "deny" diff --git a/README.md b/README.md index 73b147d..70dc57d 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ cargo fmt --all --check cargo check --workspace --all-targets cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace --all-targets -cargo check -p ely_servo_host --features servo-engine,hardware-render --all-targets -cargo clippy -p ely_servo_host --features servo-engine,hardware-render --all-targets -- -D warnings +cargo check -p ely_servo_host --features servo-engine --all-targets +cargo clippy -p ely_servo_host --features servo-engine --all-targets -- -D warnings cargo test -p ely_servo_host --features servo-engine --test software_host scripts/verify_prd_site_rendering.sh scripts/verify_windows_app_manifest.sh diff --git a/crates/ely_app/Cargo.toml b/crates/ely_app/Cargo.toml index 7e764a3..bd44f09 100644 --- a/crates/ely_app/Cargo.toml +++ b/crates/ely_app/Cargo.toml @@ -15,6 +15,7 @@ ed25519-dalek.workspace = true ely_browser_core = { path = "../ely_browser_core" } ely_design_system = { path = "../ely_design_system" } ely_domain = { path = "../ely_domain" } +ely_servo_host = { path = "../ely_servo_host", features = ["servo-engine"] } ely_sync_client = { path = "../ely_sync_client" } gpui.workspace = true gpui-component.workspace = true @@ -29,20 +30,6 @@ tracing-subscriber.workspace = true ureq.workspace = true url.workspace = true -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.10" -# Pin to the same minor as gpui 0.2.2 so we share its `CVPixelBuffer` -# type — gpui's `Surface::From` only matches the -# version it built against. -core-video = "0.4" -io-surface = "0.16" -mach2 = "0.6" -objc2 = "0.6" -objc2-core-foundation = { version = "0.3.2", features = ["CFBase", "CFDictionary", "CFNumber", "CFString"] } -objc2-foundation = { version = "0.3.1", features = ["NSDictionary", "NSString", "NSValue"] } -objc2-io-surface = "0.3.2" -uuid.workspace = true - [dev-dependencies] gpui = { workspace = true, features = ["test-support"] } diff --git a/crates/ely_app/src/main.rs b/crates/ely_app/src/main.rs index d7eb716..44275ca 100644 --- a/crates/ely_app/src/main.rs +++ b/crates/ely_app/src/main.rs @@ -324,15 +324,17 @@ fn quit(_: &Quit, cx: &mut App) { cx.quit(); } +const DEFAULT_TRACING_FILTER: &str = "ely_app=info,ely_servo_host=warn,ely=info"; + /// Install the global tracing subscriber. `RUST_LOG` drives the -/// filter; absent it, only `warn` and above leak through so day-to-day -/// runs stay quiet. The perf target is silent by default — -/// `RUST_LOG=ely::servo::perf=info` flips on the frame-time stream -/// without touching the rest of the app. We swallow re-init errors so -/// tests that share the process state with main don't blow up. +/// filter; absent it, app-owned targets stay visible and Servo internals +/// stay opt-in. `RUST_LOG=ely::servo::perf=info` flips on the frame-time +/// stream. We swallow re-init errors so tests that share process state +/// with main can reuse this path. fn init_tracing() { use tracing_subscriber::{EnvFilter, fmt}; - let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")); + let filter = EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new(DEFAULT_TRACING_FILTER)); let _ = fmt().with_env_filter(filter).with_target(true).try_init(); } diff --git a/crates/ely_app/src/services/iosurface_mach.rs b/crates/ely_app/src/services/iosurface_mach.rs deleted file mode 100644 index 2ea50b6..0000000 --- a/crates/ely_app/src/services/iosurface_mach.rs +++ /dev/null @@ -1,233 +0,0 @@ -use std::{ - collections::BTreeMap, - ffi::CString, - mem, - time::{Duration, Instant}, -}; - -use mach2::{ - bootstrap::{bootstrap_port, bootstrap_register}, - kern_return::KERN_SUCCESS, - mach_port::{mach_port_allocate, mach_port_destroy, mach_port_insert_right}, - message::{ - MACH_MSG_PORT_DESCRIPTOR, MACH_MSG_SUCCESS, MACH_MSG_TIMEOUT_NONE, MACH_MSG_TYPE_MAKE_SEND, - MACH_RCV_MSG, MACH_RCV_TIMED_OUT, MACH_RCV_TIMEOUT, mach_msg, mach_msg_body_t, - mach_msg_header_t, mach_msg_port_descriptor_t, mach_msg_trailer_t, - }, - port::{MACH_PORT_NULL, MACH_PORT_RIGHT_RECEIVE, mach_port_t}, - traps::mach_task_self, -}; -use thiserror::Error; -use uuid::Uuid; - -const IOSURFACE_PORT_MESSAGE_ID: i32 = 0x454c_5901; -const SERVICE_PREFIX: &str = "com.ely.browser.iosurface"; - -pub(crate) struct IOSurfaceMachReceiver { - service_name: String, - receive_port: mach_port_t, - pending_ports: BTreeMap, -} - -#[derive(Debug, Error)] -pub(crate) enum IOSurfaceMachError { - #[error("Mach service name contains an interior nul byte")] - InvalidServiceName, - - #[error("mach_port_allocate returned {code}")] - AllocatePort { code: i32 }, - - #[error("mach_port_insert_right returned {code}")] - InsertSendRight { code: i32 }, - - #[error("bootstrap_register returned {code}")] - RegisterService { code: i32 }, - - #[error("mach_msg receive timed out for IOSurface surface {surface_id:#x}")] - ReceiveTimedOut { surface_id: u64 }, - - #[error("mach_msg receive returned {code}")] - Receive { code: i32 }, - - #[error("received unexpected Mach message id {message_id}")] - UnexpectedMessage { message_id: i32 }, - - #[error("received invalid IOSurface Mach message")] - InvalidMessage, -} - -impl IOSurfaceMachReceiver { - pub(crate) fn new() -> Result { - let service_name = unique_service_name(); - let service_name_c = CString::new(service_name.as_str()) - .map_err(|_| IOSurfaceMachError::InvalidServiceName)?; - let mut receive_port = MACH_PORT_NULL; - - #[expect(unsafe_code)] - let task = unsafe { mach_task_self() }; - #[expect(unsafe_code)] - let allocate = - unsafe { mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &mut receive_port) }; - if allocate != KERN_SUCCESS { - return Err(IOSurfaceMachError::AllocatePort { code: allocate }); - } - - #[expect(unsafe_code)] - let insert = unsafe { - mach_port_insert_right(task, receive_port, receive_port, MACH_MSG_TYPE_MAKE_SEND) - }; - if insert != KERN_SUCCESS { - destroy_port(receive_port); - return Err(IOSurfaceMachError::InsertSendRight { code: insert }); - } - - #[expect(unsafe_code)] - #[allow(deprecated)] - let register = unsafe { - bootstrap_register(bootstrap_port, service_name_c.as_ptr() as *mut _, receive_port) - }; - if register != KERN_SUCCESS { - destroy_port(receive_port); - return Err(IOSurfaceMachError::RegisterService { code: register }); - } - - Ok(Self { service_name, receive_port, pending_ports: BTreeMap::new() }) - } - - pub(crate) fn service_name(&self) -> &str { - self.service_name.as_str() - } - - pub(crate) fn receive_port_for_surface( - &mut self, - surface_id: u64, - timeout: Duration, - ) -> Result { - if let Some(port) = self.pending_ports.remove(&surface_id) { - return Ok(port); - } - - let deadline = Instant::now() + timeout; - loop { - let remaining = deadline.saturating_duration_since(Instant::now()); - if remaining.is_zero() { - return Err(IOSurfaceMachError::ReceiveTimedOut { surface_id }); - } - let Some(received) = self.receive_one(remaining)? else { - return Err(IOSurfaceMachError::ReceiveTimedOut { surface_id }); - }; - if received.surface_id == surface_id { - return Ok(received.mach_port); - } - self.pending_ports.insert(received.surface_id, received.mach_port); - } - } - - fn receive_one( - &self, - timeout: Duration, - ) -> Result, IOSurfaceMachError> { - #[expect(unsafe_code)] - let mut received_message: ReceivedIOSurfacePortMessage = unsafe { mem::zeroed() }; - let timeout_ms = timeout_millis(timeout); - #[expect(unsafe_code)] - let result = unsafe { - mach_msg( - &mut received_message.message.header, - MACH_RCV_MSG | MACH_RCV_TIMEOUT, - 0, - mem::size_of::() as u32, - self.receive_port, - timeout_ms, - MACH_PORT_NULL, - ) - }; - - if result == MACH_RCV_TIMED_OUT { - return Ok(None); - } - if result != MACH_MSG_SUCCESS { - return Err(IOSurfaceMachError::Receive { code: result }); - } - let message = &mut received_message.message; - if message.header.msgh_id != IOSURFACE_PORT_MESSAGE_ID { - destroy_message(message); - return Err(IOSurfaceMachError::UnexpectedMessage { - message_id: message.header.msgh_id, - }); - } - if message.body.msgh_descriptor_count != 1 - || message.surface_port.type_ != MACH_MSG_PORT_DESCRIPTOR as u8 - || message.surface_port.name == MACH_PORT_NULL - { - destroy_message(message); - return Err(IOSurfaceMachError::InvalidMessage); - } - - Ok(Some(ReceivedSurfacePort { - surface_id: message.surface_id, - mach_port: message.surface_port.name, - })) - } -} - -impl Drop for IOSurfaceMachReceiver { - fn drop(&mut self) { - for port in std::mem::take(&mut self.pending_ports).into_values() { - deallocate_port(port); - } - destroy_port(self.receive_port); - } -} - -struct ReceivedSurfacePort { - surface_id: u64, - mach_port: mach_port_t, -} - -#[repr(C)] -struct IOSurfacePortMessage { - header: mach_msg_header_t, - body: mach_msg_body_t, - surface_port: mach_msg_port_descriptor_t, - surface_id: u64, -} - -#[repr(C)] -struct ReceivedIOSurfacePortMessage { - message: IOSurfacePortMessage, - _trailer: mach_msg_trailer_t, -} - -fn unique_service_name() -> String { - format!("{SERVICE_PREFIX}.{}", Uuid::now_v7().as_simple()) -} - -fn timeout_millis(timeout: Duration) -> u32 { - u32::try_from(timeout.as_millis()).unwrap_or(u32::MAX).max(MACH_MSG_TIMEOUT_NONE + 1) -} - -fn destroy_message(message: &mut IOSurfacePortMessage) { - #[expect(unsafe_code)] - unsafe { - mach2::message::mach_msg_destroy(&mut message.header); - } -} - -fn destroy_port(port: mach_port_t) { - #[expect(unsafe_code)] - let task = unsafe { mach_task_self() }; - #[expect(unsafe_code)] - unsafe { - let _ = mach_port_destroy(task, port); - } -} - -fn deallocate_port(port: mach_port_t) { - #[expect(unsafe_code)] - let task = unsafe { mach_task_self() }; - #[expect(unsafe_code)] - unsafe { - let _ = mach2::mach_port::mach_port_deallocate(task, port); - } -} diff --git a/crates/ely_app/src/services/iosurface_metal.rs b/crates/ely_app/src/services/iosurface_metal.rs deleted file mode 100644 index 38001fc..0000000 --- a/crates/ely_app/src/services/iosurface_metal.rs +++ /dev/null @@ -1,306 +0,0 @@ -//! macOS-only import of cross-process IOSurface handles into -//! `CVPixelBuffer`s that preserve the sidecar's IOSurface identity. -//! -//! `T10.4` originally imported the IOSurface into an `MTLTexture` -//! directly. GPUI 0.2.2 exposes `Window::paint_surface` / -//! `elements::surface::Surface` for `CVPixelBuffer`; the local GPUI -//! patch adds a BGRA fragment pipeline for Servo's hardware -//! IOSurfaces, so this cache is the renderer-side handoff point. -//! -//! Lifetime contract: -//! -//! * `IOSurfaceCreateMachPort` (sidecar side) gives the receiver a -//! send right whose refcount is 1 in our task. After we resolve -//! the surface and wrap it in a CVPixelBuffer, the mach port has -//! done its job. -//! * `CVPixelBufferCreateWithIOSurface` retains the IOSurface for -//! the pixel buffer's lifetime. We `mach_port_deallocate` -//! immediately so the receiver process doesn't accumulate idle -//! mach send rights. -//! * Dropping `IOSurfaceCache` releases every cached -//! `CVPixelBuffer`, which in turn releases each retained -//! IOSurface. The sidecar still holds its own retain via surfman, -//! so the IOSurface itself outlives our cache for as long as the -//! sidecar keeps painting. - -#![cfg(target_os = "macos")] - -use std::collections::HashMap; - -use core_foundation::base::TCFType as _; -use core_video::pixel_buffer::CVPixelBuffer; -#[allow(deprecated)] -use io_surface::IOSurface; -use thiserror::Error; - -/// Cache of imported `CVPixelBuffer`s keyed by IOSurface identity. -/// Constructed lazily by the renderer-side client on the first -/// hardware-path frame. -pub(crate) struct IOSurfaceCache { - pixel_buffers: HashMap, -} - -// SAFETY: CVPixelBuffer wraps CVPixelBufferRef, a CoreFoundation type -// Apple documents as safe to share across threads. The cache is owned -// by ServoLiveClient which now lives on the LiveRuntimeWorker thread, -// so the auto-Send check (rightly) rejects the raw pointer inside the -// crate's `CVPixelBuffer`. The pointer is atomically refcounted CFTypeRef -// and only mutated via Mach IPC, which is itself thread-safe. -#[expect(unsafe_code)] -unsafe impl Send for IOSurfaceCache {} - -struct CachedPixelBuffer { - pixel_buffer: CVPixelBuffer, - width: u32, - height: u32, -} - -#[derive(Debug, Error)] -pub(crate) enum SurfaceImportError { - #[error("IOSurfaceLookupFromMachPort returned null for port 0x{port:x}")] - LookupFailed { port: u32 }, - #[error("CVPixelBufferCreateWithIOSurface returned status {status}")] - PixelBufferBuildFailed { status: i32 }, -} - -impl IOSurfaceCache { - pub fn new() -> Self { - Self { pixel_buffers: HashMap::new() } - } - - /// Import an IOSurface published by the sidecar's - /// `surface_handle` field. Idempotent on `surface_id` plus pixel - /// dimensions: duplicate handles for the same sized IOSurface are - /// discarded, while a resized IOSurface that reuses the same - /// `surface_id` replaces the cached pixel buffer. - #[cfg(test)] - pub fn import( - &mut self, - mach_port_name: u32, - surface_id: u64, - ) -> Result<(), SurfaceImportError> { - let pixel_buffer = import_pixel_buffer_from_mach_port(mach_port_name)?; - self.insert_pixel_buffer(surface_id, pixel_buffer); - Ok(()) - } - - pub(crate) fn insert_pixel_buffer(&mut self, surface_id: u64, pixel_buffer: CVPixelBuffer) { - let width = pixel_buffer.get_width() as u32; - let height = pixel_buffer.get_height() as u32; - - if self - .pixel_buffers - .get(&surface_id) - .is_some_and(|cached| cached.width == width && cached.height == height) - { - return; - } - - self.pixel_buffers.insert(surface_id, CachedPixelBuffer { pixel_buffer, width, height }); - } - - /// Look up an already-imported pixel buffer by `surface_id`. The - /// receiver's per-frame `current_surface_id` selects which of the - /// swap chain's rotating front/back surfaces to sample. Returns a - /// clone (CVPixelBuffer is reference-counted; cloning is a cheap - /// atomic increment) so the caller can hand it to GPUI's - /// `surface(...)` element without holding a borrow on the cache. - pub fn pixel_buffer_for(&self, surface_id: u64) -> Option { - self.pixel_buffers.get(&surface_id).map(|cached| cached.pixel_buffer.clone()) - } - - pub(crate) fn surface_ids(&self) -> Vec { - self.pixel_buffers.keys().copied().collect() - } - - #[cfg(test)] - pub fn cached_surface_count(&self) -> usize { - self.pixel_buffers.len() - } -} - -pub(crate) fn import_pixel_buffer_from_mach_port( - mach_port_name: u32, -) -> Result { - let result = build_pixel_buffer_from_mach_port(mach_port_name); - deallocate_mach_port(mach_port_name); - result -} - -fn build_pixel_buffer_from_mach_port( - mach_port_name: u32, -) -> Result { - let Some(iosurface) = objc2_io_surface::IOSurfaceRef::lookup_from_mach_port(mach_port_name) - else { - return Err(SurfaceImportError::LookupFailed { port: mach_port_name }); - }; - - // Both objc2-io-surface and the legacy `io_surface` crate wrap - // the same C `__IOSurface` pointer. CVPixelBufferCreateWithIOSurface - // (via core-video) expects the legacy crate's wrapper. Reach for - // the raw pointer and let TCFType CFRetain it independently so - // both Rust handles can drop without double-freeing. - let raw_ptr: *const std::ffi::c_void = - (&*iosurface) as *const objc2_io_surface::IOSurfaceRef as *const std::ffi::c_void; - #[allow(deprecated)] - let io_surface_view: IOSurface = { - #[expect(unsafe_code)] - unsafe { - IOSurface::wrap_under_get_rule(raw_ptr as io_surface::IOSurfaceRef) - } - }; - - CVPixelBuffer::from_io_surface(&io_surface_view, None) - .map_err(|status| SurfaceImportError::PixelBufferBuildFailed { status }) -} - -/// Release one send right against the mach port we received. The -/// IOSurface itself stays alive because the `CVPixelBuffer` (or the -/// sidecar's surfman) still retain it. -fn deallocate_mach_port(port: u32) { - #[expect(unsafe_code)] - let result = unsafe { mach_port_deallocate(mach_task_self_, port) }; - if result != KERN_SUCCESS { - tracing::warn!( - target: "ely::servo::iosurface", - mach_port_name = port, - kern_result = result, - "mach_port_deallocate returned non-success", - ); - } -} - -const KERN_SUCCESS: i32 = 0; - -#[expect(unsafe_code)] -unsafe extern "C" { - /// Global mach task port for the running process. Defined in - /// `mach/mach_init.h` as `extern mach_port_t mach_task_self_;`. - static mach_task_self_: u32; - - /// Releases one send right against `name` within `task`. We only - /// ever call this with our own task; the IOSurface keeps its - /// retain via the CVPixelBuffer so this just frees our port slot. - fn mach_port_deallocate(task: u32, name: u32) -> i32; -} - -#[cfg(test)] -mod tests { - use super::IOSurfaceCache; - use objc2_core_foundation::{ - CFDictionary, CFIndex, CFNumber, CFRetained, CFString, kCFAllocatorDefault, - kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks, - }; - use objc2_io_surface::{ - IOSurfaceRef, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow, kIOSurfaceHeight, - kIOSurfacePixelFormat, kIOSurfaceWidth, - }; - use std::os::raw::c_void; - - /// Build a CPU-backed IOSurface from scratch, the same way - /// surfman's macOS backend does. - /// - /// BGRA8 (four-cc '32BGRA'), width + height + bytes_per_element - /// + bytes_per_row live in a Core Foundation properties dictionary. - /// - /// The pointer-casts mirror - /// `surfman::platform::macos::system::surface::create_io_surface`. - fn build_local_iosurface(width: u32, height: u32) -> Result, String> { - let pixel_format: i32 = i32::from_be_bytes(*b"BGRA"); - let bytes_per_element: i32 = 4; - let bytes_per_row: i32 = (width as i32) * bytes_per_element; - - let width_num = CFNumber::new_i32(width as i32); - let height_num = CFNumber::new_i32(height as i32); - let bpe_num = CFNumber::new_i32(bytes_per_element); - let bpr_num = CFNumber::new_i32(bytes_per_row); - let pf_num = CFNumber::new_i32(pixel_format); - - #[expect(unsafe_code)] - unsafe { - let keys: [&CFString; 5] = [ - kIOSurfaceWidth, - kIOSurfaceHeight, - kIOSurfaceBytesPerElement, - kIOSurfaceBytesPerRow, - kIOSurfacePixelFormat, - ]; - let values: [&CFNumber; 5] = [&width_num, &height_num, &bpe_num, &bpr_num, &pf_num]; - let keys_ptr: *mut *const c_void = keys.as_ptr() as *mut *const c_void; - let values_ptr: *mut *const c_void = values.as_ptr() as *mut *const c_void; - let properties = CFDictionary::new( - kCFAllocatorDefault, - keys_ptr, - values_ptr, - keys.len() as CFIndex, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks, - ) - .ok_or_else(|| "CFDictionaryCreate returned null".to_string())?; - IOSurfaceRef::new(&properties) - .ok_or_else(|| "IOSurfaceCreate returned null".to_string()) - } - } - - #[test] - fn imports_local_iosurface_into_pixel_buffer() -> Result<(), String> { - let mut cache = IOSurfaceCache::new(); - let iosurface = build_local_iosurface(64, 48)?; - let mach_port = iosurface.create_mach_port(); - assert!(mach_port != 0, "IOSurfaceCreateMachPort must yield a real port"); - let surface_id: u64 = 0xDEAD_BEEFu64; - - cache.import(mach_port, surface_id).map_err(|error| error.to_string())?; - - let pixel_buffer = cache - .pixel_buffer_for(surface_id) - .ok_or_else(|| "imported pixel buffer was missing".to_string())?; - assert_eq!( - pixel_buffer.get_width() as u32, - 64, - "CVPixelBuffer width must match the source IOSurface", - ); - assert_eq!( - pixel_buffer.get_height() as u32, - 48, - "CVPixelBuffer height must match the source IOSurface", - ); - assert_eq!(cache.cached_surface_count(), 1); - Ok(()) - } - - #[test] - fn second_import_with_same_surface_id_is_idempotent() -> Result<(), String> { - let mut cache = IOSurfaceCache::new(); - let iosurface = build_local_iosurface(64, 48)?; - let port_a = iosurface.create_mach_port(); - let port_b = iosurface.create_mach_port(); - assert!(port_a != 0 && port_b != 0 && port_a != port_b); - - cache.import(port_a, 0xAAAA_AAAA).map_err(|error| error.to_string())?; - // Same surface_id → defensive dedup path; port_b is deallocated - // without minting a duplicate CVPixelBuffer. - cache.import(port_b, 0xAAAA_AAAA).map_err(|error| error.to_string())?; - assert_eq!(cache.cached_surface_count(), 1); - Ok(()) - } - - #[test] - fn same_surface_id_with_changed_dimensions_replaces_pixel_buffer() -> Result<(), String> { - let mut cache = IOSurfaceCache::new(); - let initial = build_local_iosurface(64, 48)?; - let resized = build_local_iosurface(96, 72)?; - let surface_id = 0xBBBB_BBBB; - - cache.import(initial.create_mach_port(), surface_id).map_err(|error| error.to_string())?; - cache.import(resized.create_mach_port(), surface_id).map_err(|error| error.to_string())?; - - let pixel_buffer = cache - .pixel_buffer_for(surface_id) - .ok_or_else(|| "resized pixel buffer was missing".to_string())?; - assert_eq!(pixel_buffer.get_width() as u32, 96); - assert_eq!(pixel_buffer.get_height() as u32, 72); - assert_eq!(cache.cached_surface_count(), 1); - Ok(()) - } -} diff --git a/crates/ely_app/src/services/mod.rs b/crates/ely_app/src/services/mod.rs index ee2483f..288f835 100644 --- a/crates/ely_app/src/services/mod.rs +++ b/crates/ely_app/src/services/mod.rs @@ -1,16 +1,11 @@ pub mod download_checksums; pub mod download_files; pub mod http_downloads; -#[cfg(target_os = "macos")] -pub(crate) mod iosurface_mach; -#[cfg(target_os = "macos")] -pub(crate) mod iosurface_metal; pub mod plugin_package_store; pub mod plugin_packages; pub mod plugin_signatures; pub mod servo_live; pub(crate) mod servo_profile_data; -mod servo_sidecar_command; pub(crate) use servo_profile_data::ProfileDataMode; diff --git a/crates/ely_app/src/services/servo_live.rs b/crates/ely_app/src/services/servo_live.rs index 0efcd25..7f07653 100644 --- a/crates/ely_app/src/services/servo_live.rs +++ b/crates/ely_app/src/services/servo_live.rs @@ -1,286 +1,300 @@ -use std::{ - io::{BufRead, BufReader, Read, Write}, - path::PathBuf, - process::{Child, ChildStdin, ChildStdout, Stdio}, +use std::{collections::BTreeMap, path::PathBuf}; + +use ely_domain::{ + ProfileId, SiteOrigin, SitePermissionDecision, SitePermissionFeature, TabId, UrlText, WebViewId, +}; +use ely_servo_host::{ + HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseHoverRequest, + NavigationRequest, PageZoomRequest, PermissionDecision, PermissionRequest, ResizeRequest, + ScrollRequest, ServoHost, ServoSurfaceSize, SoftwareServoHost, }; -#[cfg(target_os = "macos")] -#[path = "servo_live_iosurface_importer.rs"] -mod iosurface_importer; #[path = "servo_live_types.rs"] mod types; -/// Environment variable that lets the user pick the rendering context -/// kind used by the spawned sidecar. Accepted values: `software` -/// and `hardware`. macOS defaults to the hardware path and receives -/// IOSurface mach send rights over a side Mach channel. -#[path = "servo_live_wire.rs"] -mod wire; pub(crate) use types::{ ServoLiveEnsureRequest, ServoLiveError, ServoLiveFrame, ServoLiveSitePermission, }; -use super::servo_sidecar_command::{ - SidecarRenderingContext, default_sidecar_command, rendering_context_from_env, -}; -use wire::{ - LiveRequest, LiveResponse, LiveSurfaceHandle, log_frame_perf, log_iosurface_current, - log_iosurface_handle, -}; - -#[cfg(target_os = "macos")] -use super::iosurface_metal::IOSurfaceCache; -#[cfg(target_os = "macos")] -use iosurface_importer::{IOSurfaceImportResult, IOSurfaceImportWorker}; - pub(crate) struct ServoLiveClient { - child: Child, - stdin: ChildStdin, - stdout: BufReader, - /// Cache of imported `CVPixelBuffer`s keyed by surface_id. Built - /// lazily on the first `surface_handle` the sidecar publishes — - /// software-path tabs never trigger construction. - #[cfg(target_os = "macos")] - iosurface_cache: IOSurfaceCache, - #[cfg(target_os = "macos")] - iosurface_importer: Option, + host: SoftwareServoHost, + sessions: BTreeMap, } impl ServoLiveClient { pub fn new(profile_data_dir: PathBuf) -> Result { - let command_target = default_sidecar_command()?; - if let Some(path) = command_target.missing_binary_path() { - return Err(ServoLiveError::SidecarBinaryUnavailable { path: path.to_path_buf() }); - } - - let rendering_context = rendering_context_from_env(); - let mut command = command_target.command(); - command.arg("live").arg("--profile-data-dir").arg(profile_data_dir); - command.arg("--rendering-context").arg(rendering_context.cli_arg()); - #[cfg(target_os = "macos")] - let iosurface_importer = if rendering_context == SidecarRenderingContext::Hardware { - let receiver = super::iosurface_mach::IOSurfaceMachReceiver::new()?; - command.arg("--iosurface-mach-service").arg(receiver.service_name()); - Some( - IOSurfaceImportWorker::new(receiver) - .map_err(ServoLiveError::IOSurfaceImportWorker)?, - ) - } else { - None - }; - let mut child = command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn() - .map_err(ServoLiveError::Command)?; - let stdin = child.stdin.take().ok_or(ServoLiveError::PipeUnavailable { name: "stdin" })?; - let stdout = - child.stdout.take().ok_or(ServoLiveError::PipeUnavailable { name: "stdout" })?; - - Ok(Self { - child, - stdin, - stdout: BufReader::new(stdout), - #[cfg(target_os = "macos")] - iosurface_cache: IOSurfaceCache::new(), - #[cfg(target_os = "macos")] - iosurface_importer, - }) + let host = SoftwareServoHost::new_with_config_dir( + ServoSurfaceSize::new(1, 1), + Some(profile_data_dir), + )?; + Ok(Self { host, sessions: BTreeMap::new() }) } pub fn ensure( &mut self, request: ServoLiveEnsureRequest, ) -> Result, ServoLiveError> { - #[cfg(target_os = "macos")] - self.drain_iosurface_imports()?; - let ready_surface_ids = self.ready_surface_ids(); - self.request(LiveRequest::Ensure { - tab_id: request.tab_id, - profile_id: request.profile_id, - url: request.url, - width: request.width, - height: request.height, - page_zoom_percent: request.page_zoom_percent, - device_pixel_ratio: request.device_pixel_ratio, - scroll_delta_x: request.scroll_delta_x, - scroll_delta_y: request.scroll_delta_y, - scroll_point_x: request.scroll_point_x, - scroll_point_y: request.scroll_point_y, - click_x: request.click_x, - click_y: request.click_y, - hover_x: request.hover_x, - hover_y: request.hover_y, - typed_text: request.typed_text, - site_permissions: request.site_permissions, - ready_surface_ids, - }) + let tab_id = TabId::parse(request.tab_id.clone())?; + let profile_id = ProfileId::parse(request.profile_id.clone())?; + let requested_url = UrlText::parse(request.url.clone())?; + let webview_id = self.ensure_webview(&request, &tab_id, &profile_id)?; + + self.apply_viewport(&request, &webview_id)?; + self.apply_permissions(&request, &webview_id, &profile_id)?; + self.apply_navigation(&request, &webview_id, tab_id, requested_url)?; + self.apply_input(&request, &webview_id)?; + self.host.tick(); + if !self.session_uses_native_surface(&request.tab_id) { + return Err(ServoLiveError::NativeSurfaceUnavailable); + } + self.host.paint_without_readback_with_completion(&webview_id, false)?; + + let frame = self.frame_from_session(&request.tab_id, &webview_id)?; + Ok(Some(frame)) } pub fn poll(&mut self, tab_id: String) -> Result, ServoLiveError> { - #[cfg(target_os = "macos")] - self.drain_iosurface_imports()?; - let ready_surface_ids = self.ready_surface_ids(); - self.request(LiveRequest::Poll { tab_id, ready_surface_ids }) + let Some(session) = self.sessions.get(&tab_id) else { + return Ok(None); + }; + let webview_id = session.webview_id.clone(); + let uses_native_surface = session.native_surface_id.is_some(); + + self.host.tick(); + if !self.host.snapshot(&webview_id)?.has_pending_frame() { + return Ok(None); + } + + if !uses_native_surface { + return Err(ServoLiveError::NativeSurfaceUnavailable); + } + self.host.paint_without_readback_with_completion(&webview_id, false)?; + self.frame_from_session(&tab_id, &webview_id).map(Some) } pub fn close(&mut self, tab_id: String) -> Result<(), ServoLiveError> { - self.request(LiveRequest::Close { tab_id }).map(|_| ()) - } - - fn request(&mut self, request: LiveRequest) -> Result, ServoLiveError> { - serde_json::to_writer(&mut self.stdin, &request)?; - self.stdin.write_all(b"\n").map_err(ServoLiveError::Command)?; - self.stdin.flush().map_err(ServoLiveError::Command)?; - - let mut line = String::new(); - let bytes = self.stdout.read_line(&mut line).map_err(ServoLiveError::Command)?; - if bytes == 0 { - return Err(ServoLiveError::SidecarExited); - } - - let response: LiveResponse = serde_json::from_str(&line)?; - if let Some(error) = response.error { - return Err(ServoLiveError::SidecarFailed { message: error }); - } - let surface_handle = response.surface_handle; - let current_surface_id = response.current_surface_id; - - if let Some(perf) = response.perf.as_ref() { - log_frame_perf(perf); - } - - if let Some(handle) = surface_handle.as_ref() { - log_iosurface_handle(handle); - } - if let Some(surface_id) = current_surface_id { - log_iosurface_current(surface_id); - } - - let Some(report) = response.frame else { - return Ok(None); - }; - - // Sanity bound the byte count advertised by the sidecar - // header so a buggy or hostile sidecar can't park us on - // `read_exact` for an arbitrarily-sized buffer. The honest - // upper limit is `width * height * 4` (RGBA8); `0` is the - // explicit "hardware path active, sample the IOSurface" - // signal; any other byte count is a protocol violation. - let pixel_byte_count = - (report.width as u64).saturating_mul(report.height as u64).saturating_mul(4); - let advertised = report.rgba_byte_count as u64; - if advertised != 0 && advertised != pixel_byte_count { - return Err(ServoLiveError::FrameBudgetExceeded { - advertised: report.rgba_byte_count, - pixel_budget: pixel_byte_count, - width: report.width, - height: report.height, - }); - } - - // Raw frame bytes follow the JSON header on the same pipe for - // software frames. `read_exact` drains BufReader's buffer first - // (the line read never crosses the `\n` boundary) and then - // pulls the rest straight from the child's stdout. - let mut rgba_bytes = vec![0u8; report.rgba_byte_count]; - if report.rgba_byte_count > 0 { - self.stdout.read_exact(&mut rgba_bytes).map_err(ServoLiveError::FrameRead)?; - } - - let has_software_payload = report.rgba_byte_count > 0; - let mut frame = ServoLiveFrame::from_parts(report, rgba_bytes); - - #[cfg(target_os = "macos")] - if let Some(handle) = surface_handle.as_ref() { - self.queue_iosurface_handle(*handle)?; - self.drain_iosurface_imports()?; - } - - #[cfg(target_os = "macos")] - if let Some(surface_id) = current_surface_id { - let pixel_buffer = self.iosurface_cache.pixel_buffer_for(surface_id); - if pixel_buffer.is_none() && !has_software_payload { - return Ok(None); - } - frame.set_pixel_buffer(pixel_buffer); - } - - Ok(Some(frame)) - } -} - -#[cfg(not(target_os = "macos"))] -impl ServoLiveClient { - fn ready_surface_ids(&self) -> Vec { - Vec::new() - } -} - -#[cfg(target_os = "macos")] -impl ServoLiveClient { - fn ready_surface_ids(&self) -> Vec { - self.iosurface_cache.surface_ids() - } - - fn queue_iosurface_handle(&mut self, handle: LiveSurfaceHandle) -> Result<(), ServoLiveError> { - let Some(importer) = self.iosurface_importer.as_ref() else { - return Err(ServoLiveError::IOSurfaceImportFailed { - surface_id: handle.surface_id, - mach_port_name: handle.mach_port_name, - message: "IOSurface import worker is unavailable".to_string(), - }); - }; - importer.submit(handle).map_err(|failure| ServoLiveError::IOSurfaceImportFailed { - surface_id: failure.surface_id, - mach_port_name: failure.mach_port_name, - message: failure.message, - }) - } - - fn drain_iosurface_imports(&mut self) -> Result<(), ServoLiveError> { - let Some(importer) = self.iosurface_importer.as_ref() else { + let Some(session) = self.sessions.remove(&tab_id) else { return Ok(()); }; - for result in importer.drain() { - match result { - IOSurfaceImportResult::Imported(imported) => { - self.iosurface_cache - .insert_pixel_buffer(imported.surface_id, imported.pixel_buffer); - tracing::info!( - target: "ely::servo::iosurface", - surface_id = imported.surface_id, - width = imported.width, - height = imported.height, - "imported IOSurface into CVPixelBuffer cache", - ); - } - IOSurfaceImportResult::Failed(failure) => { - tracing::warn!( - target: "ely::servo::iosurface", - surface_id = failure.surface_id, - width = failure.width, - height = failure.height, - mach_port_name = failure.mach_port_name, - message = %failure.message, - "IOSurface import worker failed", - ); - return Err(ServoLiveError::IOSurfaceImportFailed { - surface_id: failure.surface_id, - mach_port_name: failure.mach_port_name, - message: failure.message, - }); - } + self.host.close_webview(&session.webview_id); + Ok(()) + } + + fn ensure_webview( + &mut self, + request: &ServoLiveEnsureRequest, + tab_id: &TabId, + profile_id: &ProfileId, + ) -> Result { + if self + .sessions + .get(&request.tab_id) + .is_some_and(|session| session.profile_id != *profile_id) + && let Some(session) = self.sessions.remove(&request.tab_id) + { + self.host.close_webview(&session.webview_id); + } + + let native_surface_id = + request.native_surface.as_ref().map(gpui::NativeSurfaceHandle::identity); + if self + .sessions + .get(&request.tab_id) + .is_some_and(|session| session.native_surface_id != native_surface_id) + && let Some(session) = self.sessions.remove(&request.tab_id) + { + self.host.close_webview(&session.webview_id); + } + + if let Some(session) = self.sessions.get(&request.tab_id) { + return Ok(session.webview_id.clone()); + } + + let surface_size = ServoSurfaceSize::new(request.width, request.height); + let webview_id = match request.native_surface.as_ref() { + Some(native_surface) => self.host.create_webview_with_native_surface( + tab_id.clone(), + profile_id.clone(), + surface_size, + native_surface, + )?, + None => self.host.create_webview_with_size( + tab_id.clone(), + profile_id.clone(), + surface_size, + )?, + }; + self.sessions.insert( + request.tab_id.clone(), + DirectWebViewSession { + webview_id: webview_id.clone(), + profile_id: profile_id.clone(), + requested_url: None, + width: request.width, + height: request.height, + page_zoom_percent: request.page_zoom_percent, + device_pixel_ratio: request.device_pixel_ratio, + native_surface_id, + }, + ); + Ok(webview_id) + } + + fn apply_viewport( + &mut self, + request: &ServoLiveEnsureRequest, + webview_id: &WebViewId, + ) -> Result<(), ServoLiveError> { + let Some(session) = self.sessions.get_mut(&request.tab_id) else { + return Ok(()); + }; + + if session.width != request.width || session.height != request.height { + self.host.resize(ResizeRequest { + webview_id: webview_id.clone(), + width: request.width, + height: request.height, + })?; + session.width = request.width; + session.height = request.height; + } + + if session.device_pixel_ratio != request.device_pixel_ratio { + self.host.set_hidpi_scale(HidpiScaleRequest { + webview_id: webview_id.clone(), + scale_factor: request.device_pixel_ratio, + })?; + session.device_pixel_ratio = request.device_pixel_ratio; + } + + if session.page_zoom_percent != request.page_zoom_percent { + self.host.set_page_zoom(PageZoomRequest { + webview_id: webview_id.clone(), + zoom_factor: f32::from(request.page_zoom_percent) / 100.0, + })?; + session.page_zoom_percent = request.page_zoom_percent; + } + + Ok(()) + } + + fn apply_permissions( + &mut self, + request: &ServoLiveEnsureRequest, + webview_id: &WebViewId, + profile_id: &ProfileId, + ) -> Result<(), ServoLiveError> { + for permission in &request.site_permissions { + let origin = SiteOrigin::parse(permission.origin.clone())?; + let feature = SitePermissionFeature::parse(permission.feature.as_str())?; + let decision = SitePermissionDecision::parse(permission.decision.as_str())?; + self.host.set_permission( + PermissionRequest { + webview_id: webview_id.clone(), + profile_id: profile_id.clone(), + origin, + feature, + }, + PermissionDecision::from(decision), + )?; + } + Ok(()) + } + + fn apply_navigation( + &mut self, + request: &ServoLiveEnsureRequest, + webview_id: &WebViewId, + tab_id: TabId, + requested_url: UrlText, + ) -> Result<(), ServoLiveError> { + let should_navigate = self + .sessions + .get(&request.tab_id) + .and_then(|session| session.requested_url.as_deref()) + .is_none_or(|current| current != requested_url.as_str()); + if should_navigate { + self.host.navigate(NavigationRequest { + webview_id: webview_id.clone(), + tab_id, + url: requested_url.clone(), + })?; + if let Some(session) = self.sessions.get_mut(&request.tab_id) { + session.requested_url = Some(requested_url.as_str().to_string()); } } Ok(()) } -} -impl Drop for ServoLiveClient { - fn drop(&mut self) { - let _ = self.child.kill(); - let _ = self.child.wait(); + fn apply_input( + &mut self, + request: &ServoLiveEnsureRequest, + webview_id: &WebViewId, + ) -> Result<(), ServoLiveError> { + if request.scroll_delta_x != 0 || request.scroll_delta_y != 0 { + let point_x = request.scroll_point_x.ok_or(ServoLiveError::MissingScrollPoint)?; + let point_y = request.scroll_point_y.ok_or(ServoLiveError::MissingScrollPoint)?; + self.host.scroll(ScrollRequest { + webview_id: webview_id.clone(), + delta_x: request.scroll_delta_x, + delta_y: request.scroll_delta_y, + point_x, + point_y, + })?; + } + + if let (Some(x), Some(y)) = (request.hover_x, request.hover_y) { + self.host.hover(MouseHoverRequest { webview_id: webview_id.clone(), x, y })?; + } + + if let (Some(x), Some(y)) = (request.click_x, request.click_y) { + self.host.click(MouseClickRequest { webview_id: webview_id.clone(), x, y })?; + } + + if let Some(text) = request.typed_text.as_ref() { + self.host.type_text(KeyboardTextRequest { + webview_id: webview_id.clone(), + text: text.clone(), + })?; + } + + Ok(()) + } + + fn frame_from_session( + &self, + tab_id: &str, + webview_id: &WebViewId, + ) -> Result { + let Some(session) = self.sessions.get(tab_id) else { + return Err(ServoLiveError::Host(ely_servo_host::ServoHostError::WebViewNotFound { + id: webview_id.clone(), + })); + }; + if session.native_surface_id.is_some() { + let snapshot = self.host.snapshot(webview_id)?; + return Ok(ServoLiveFrame::from_presented( + snapshot, + session.width, + session.height, + session.device_pixel_ratio, + )); + } + Err(ServoLiveError::NativeSurfaceUnavailable) + } + + fn session_uses_native_surface(&self, tab_id: &str) -> bool { + self.sessions.get(tab_id).is_some_and(|session| session.native_surface_id.is_some()) } } + +struct DirectWebViewSession { + webview_id: WebViewId, + profile_id: ProfileId, + requested_url: Option, + width: u32, + height: u32, + page_zoom_percent: u16, + device_pixel_ratio: f32, + native_surface_id: Option, +} diff --git a/crates/ely_app/src/services/servo_live_iosurface_importer.rs b/crates/ely_app/src/services/servo_live_iosurface_importer.rs deleted file mode 100644 index 4a208e6..0000000 --- a/crates/ely_app/src/services/servo_live_iosurface_importer.rs +++ /dev/null @@ -1,147 +0,0 @@ -#![cfg(target_os = "macos")] - -use std::{ - io, - sync::mpsc, - thread::{self, JoinHandle}, - time::Duration, -}; - -use core_video::pixel_buffer::CVPixelBuffer; - -use crate::services::{ - iosurface_mach::IOSurfaceMachReceiver, iosurface_metal::import_pixel_buffer_from_mach_port, -}; - -use super::wire::LiveSurfaceHandle; - -const RECEIVE_TIMEOUT: Duration = Duration::from_secs(1); - -pub(super) struct IOSurfaceImportWorker { - request_tx: Option>, - result_rx: mpsc::Receiver, - thread: Option>, -} - -impl IOSurfaceImportWorker { - pub(super) fn new(receiver: IOSurfaceMachReceiver) -> Result { - let (request_tx, request_rx) = mpsc::channel(); - let (result_tx, result_rx) = mpsc::channel(); - let thread = thread::Builder::new() - .name("ely-iosurface-import".to_string()) - .spawn(move || run_import_worker(receiver, request_rx, result_tx))?; - - Ok(Self { request_tx: Some(request_tx), result_rx, thread: Some(thread) }) - } - - pub(super) fn submit(&self, handle: LiveSurfaceHandle) -> Result<(), IOSurfaceImportFailure> { - let Some(request_tx) = self.request_tx.as_ref() else { - return Err(IOSurfaceImportFailure::worker_stopped(handle)); - }; - request_tx.send(handle).map_err(|error| IOSurfaceImportFailure::worker_stopped(error.0)) - } - - pub(super) fn drain(&self) -> Vec { - let mut results = Vec::new(); - while let Ok(result) = self.result_rx.try_recv() { - results.push(result); - } - results - } -} - -impl Drop for IOSurfaceImportWorker { - fn drop(&mut self) { - self.request_tx.take(); - if let Some(thread) = self.thread.take() { - let _ = thread.join(); - } - } -} - -pub(super) enum IOSurfaceImportResult { - Imported(ImportedIOSurface), - Failed(IOSurfaceImportFailure), -} - -pub(super) struct ImportedIOSurface { - pub(super) surface_id: u64, - pub(super) width: u32, - pub(super) height: u32, - pub(super) pixel_buffer: CVPixelBuffer, -} - -// SAFETY: CVPixelBuffer is a CoreFoundation object with atomic -// retain/release semantics. This wrapper crosses from the importer -// thread to the live worker thread; GPUI presentation already receives -// the same handle through ServoLiveFrame's Send contract. -#[expect(unsafe_code)] -unsafe impl Send for ImportedIOSurface {} - -pub(super) struct IOSurfaceImportFailure { - pub(super) surface_id: u64, - pub(super) width: u32, - pub(super) height: u32, - pub(super) mach_port_name: u32, - pub(super) message: String, -} - -impl IOSurfaceImportFailure { - fn worker_stopped(handle: LiveSurfaceHandle) -> Self { - Self { - surface_id: handle.surface_id, - width: handle.width, - height: handle.height, - mach_port_name: handle.mach_port_name, - message: "IOSurface import worker stopped".to_string(), - } - } -} - -fn run_import_worker( - mut receiver: IOSurfaceMachReceiver, - request_rx: mpsc::Receiver, - result_tx: mpsc::Sender, -) { - while let Ok(handle) = request_rx.recv() { - let result = import_surface_handle(&mut receiver, handle); - if result_tx.send(result).is_err() { - return; - } - } -} - -fn import_surface_handle( - receiver: &mut IOSurfaceMachReceiver, - handle: LiveSurfaceHandle, -) -> IOSurfaceImportResult { - let mach_port_name = match receiver.receive_port_for_surface(handle.surface_id, RECEIVE_TIMEOUT) - { - Ok(mach_port_name) => mach_port_name, - Err(error) => { - return IOSurfaceImportResult::Failed(IOSurfaceImportFailure { - surface_id: handle.surface_id, - width: handle.width, - height: handle.height, - mach_port_name: handle.mach_port_name, - message: error.to_string(), - }); - } - }; - - match import_pixel_buffer_from_mach_port(mach_port_name) { - Ok(pixel_buffer) => IOSurfaceImportResult::Imported(ImportedIOSurface { - surface_id: handle.surface_id, - width: handle.width, - height: handle.height, - pixel_buffer, - }), - Err(error) => IOSurfaceImportResult::Failed(IOSurfaceImportFailure { - surface_id: handle.surface_id, - width: handle.width, - height: handle.height, - mach_port_name, - message: error.to_string(), - }), - } -} diff --git a/crates/ely_app/src/services/servo_live_types.rs b/crates/ely_app/src/services/servo_live_types.rs index db9f0e4..145c164 100644 --- a/crates/ely_app/src/services/servo_live_types.rs +++ b/crates/ely_app/src/services/servo_live_types.rs @@ -1,17 +1,9 @@ -use std::{io, path::PathBuf}; - use ely_domain::SitePermissionDecision; +use ely_servo_host::{ServoHostError, WebViewSnapshot, WebViewState}; +use gpui::NativeSurfaceHandle; use serde::Serialize; use thiserror::Error; -use super::wire::LiveFrameReport; -use crate::services::servo_sidecar_command::SidecarCommandError; - -#[cfg(target_os = "macos")] -use crate::services::iosurface_mach::IOSurfaceMachError; -#[cfg(target_os = "macos")] -use core_video::pixel_buffer::CVPixelBuffer; - pub(crate) struct ServoLiveEnsureRequest { pub(crate) tab_id: String, pub(crate) profile_id: String, @@ -20,10 +12,9 @@ pub(crate) struct ServoLiveEnsureRequest { pub(crate) height: u32, pub(crate) page_zoom_percent: u16, /// Display scale factor (1.0 standard, 2.0 Retina). Servo's - /// WebView lays out CSS pixels = device pixels / hidpi factor; - /// without this, a Retina viewport gets desktop-CSS-pixel layout - /// and every visible element renders at half its expected size. + /// WebView lays out CSS pixels = device pixels / hidpi factor. pub(crate) device_pixel_ratio: f32, + pub(crate) native_surface: Option, pub(crate) scroll_delta_x: i32, pub(crate) scroll_delta_y: i32, pub(crate) scroll_point_x: Option, @@ -68,54 +59,37 @@ pub(crate) struct ServoLiveFrame { content_pixel_count: u64, #[cfg(all(test, feature = "live-site-smoke"))] sample_hash: u64, - rgba_bytes: Vec, - #[cfg(target_os = "macos")] - pixel_buffer: Option, + rgba_bytes: Option>, } -// SAFETY: CVPixelBuffer wraps CVPixelBufferRef, a CoreFoundation type -// Apple documents as safe to share across threads. The Rust core-video -// crate does not mark it Send, so the worker thread needs this opt-in -// to ship hardware frames back to the UI thread via mpsc::Sender. -#[cfg(target_os = "macos")] -#[expect(unsafe_code)] -unsafe impl Send for ServoLiveFrame {} - impl ServoLiveFrame { - pub(super) fn from_parts(report: LiveFrameReport, rgba_bytes: Vec) -> Self { - let (css_viewport_width, css_viewport_height) = css_viewport_size_from_report(&report); + pub(super) fn from_presented( + snapshot: WebViewSnapshot, + width: u32, + height: u32, + device_pixel_ratio: f32, + ) -> Self { + let (css_viewport_width, css_viewport_height) = + css_viewport_size(width, height, device_pixel_ratio); Self { - loaded_url: report.loaded_url, - title: report.title, - render_state: report.state, - width: report.width, - height: report.height, - device_pixel_ratio: report.device_pixel_ratio, + loaded_url: snapshot.url().map(str::to_string), + title: snapshot.title().map(str::to_string), + render_state: render_state_label(snapshot.state()).to_string(), + width, + height, + device_pixel_ratio, css_viewport_width, css_viewport_height, #[cfg(all(test, feature = "live-site-smoke"))] - non_white_pixel_count: report.non_white_pixel_count, + non_white_pixel_count: 1, #[cfg(all(test, feature = "live-site-smoke"))] - content_pixel_count: report.content_pixel_count, + content_pixel_count: 1, #[cfg(all(test, feature = "live-site-smoke"))] - sample_hash: report.sample_hash, - rgba_bytes, - #[cfg(target_os = "macos")] - pixel_buffer: None, + sample_hash: 0, + rgba_bytes: None, } } - #[cfg(target_os = "macos")] - pub(super) fn set_pixel_buffer(&mut self, pixel_buffer: Option) { - self.pixel_buffer = pixel_buffer; - } - - #[cfg(target_os = "macos")] - #[must_use] - pub fn pixel_buffer(&self) -> Option<&CVPixelBuffer> { - self.pixel_buffer.as_ref() - } - #[must_use] pub fn loaded_url(&self) -> Option<&str> { self.loaded_url.as_deref() @@ -175,7 +149,7 @@ impl ServoLiveFrame { } #[must_use] - pub fn into_rgba_bytes(self) -> Vec { + pub fn into_rgba_bytes(self) -> Option> { self.rgba_bytes } @@ -196,113 +170,50 @@ impl ServoLiveFrame { content_pixel_count: 0, #[cfg(all(test, feature = "live-site-smoke"))] sample_hash: 0, - rgba_bytes, - #[cfg(target_os = "macos")] - pixel_buffer: None, - } - } - - #[cfg(all(test, target_os = "macos"))] - pub(crate) fn for_test_with_pixel_buffer( - width: u32, - height: u32, - pixel_buffer: CVPixelBuffer, - ) -> Self { - Self { - loaded_url: Some("https://example.com/".to_string()), - title: Some("Example".to_string()), - render_state: "complete".to_string(), - width, - height, - device_pixel_ratio: 1.0, - css_viewport_width: width, - css_viewport_height: height, - #[cfg(all(test, feature = "live-site-smoke"))] - non_white_pixel_count: 0, - #[cfg(all(test, feature = "live-site-smoke"))] - content_pixel_count: 0, - #[cfg(all(test, feature = "live-site-smoke"))] - sample_hash: 0, - rgba_bytes: Vec::new(), - pixel_buffer: Some(pixel_buffer), + rgba_bytes: Some(rgba_bytes), } } } -fn css_viewport_size_from_report(report: &LiveFrameReport) -> (u32, u32) { - let dpr = if report.device_pixel_ratio.is_finite() && report.device_pixel_ratio > 0.0 { - report.device_pixel_ratio +fn render_state_label(state: &WebViewState) -> &'static str { + match state { + WebViewState::Created => "created", + WebViewState::Loading => "loading", + WebViewState::Complete => "complete", + WebViewState::Sleeping => "sleeping", + WebViewState::Crashed => "crashed", + } +} + +fn css_viewport_size(width: u32, height: u32, device_pixel_ratio: f32) -> (u32, u32) { + let scale = if device_pixel_ratio.is_finite() && device_pixel_ratio > 0.0 { + device_pixel_ratio } else { 1.0 }; - let fallback_width = ((report.width as f32) / dpr).round().max(1.0) as u32; - let fallback_height = ((report.height as f32) / dpr).round().max(1.0) as u32; ( - if report.css_viewport_width > 0 { report.css_viewport_width } else { fallback_width }, - if report.css_viewport_height > 0 { report.css_viewport_height } else { fallback_height }, + ((width as f32) / scale).round().max(1.0) as u32, + ((height as f32) / scale).round().max(1.0) as u32, ) } #[derive(Debug, Error)] pub(crate) enum ServoLiveError { - #[error("servo sidecar binary is unavailable at {path}")] - SidecarBinaryUnavailable { path: PathBuf }, + #[error("servo native surface is unavailable")] + NativeSurfaceUnavailable, - #[error("failed to run servo live sidecar: {0}")] - Command(#[source] io::Error), - - #[error("servo live sidecar pipe is unavailable: {name}")] - PipeUnavailable { name: &'static str }, - - #[error("servo live sidecar exited")] - SidecarExited, - - #[error("servo live sidecar failed: {message}")] - SidecarFailed { message: String }, - - #[error("failed to read servo live frame bytes: {0}")] - FrameRead(#[source] io::Error), - - #[error( - "servo live sidecar advertised {advertised} frame bytes which exceeds \ - the {width}x{height} pixel budget ({pixel_budget} bytes)" - )] - FrameBudgetExceeded { advertised: usize, pixel_budget: u64, width: u32, height: u32 }, - - #[cfg(target_os = "macos")] - #[error( - "servo live IOSurface import failed for surface {surface_id:#x} \ - mach port 0x{mach_port_name:x}: {message}" - )] - IOSurfaceImportFailed { surface_id: u64, mach_port_name: u32, message: String }, - - #[cfg(target_os = "macos")] - #[error("failed to spawn servo live IOSurface importer: {0}")] - IOSurfaceImportWorker(#[source] io::Error), - - #[cfg(target_os = "macos")] - #[error(transparent)] - IOSurfaceMach(#[from] IOSurfaceMachError), + #[error("servo scroll input is missing a viewport point")] + MissingScrollPoint, #[error(transparent)] - Json(#[from] serde_json::Error), + Domain(#[from] ely_domain::DomainError), #[error(transparent)] - SidecarCommand(#[from] SidecarCommandError), + Host(#[from] ServoHostError), } impl ServoLiveError { - pub(crate) fn is_sidecar_process_unusable(&self) -> bool { - match self { - Self::SidecarExited => true, - Self::Command(error) | Self::FrameRead(error) => matches!( - error.kind(), - io::ErrorKind::BrokenPipe - | io::ErrorKind::ConnectionAborted - | io::ErrorKind::ConnectionReset - | io::ErrorKind::UnexpectedEof - ), - _ => false, - } + pub(crate) fn is_runtime_unavailable(&self) -> bool { + matches!(self, Self::Host(ServoHostError::RuntimeAlreadyStarted)) } } diff --git a/crates/ely_app/src/services/servo_live_wire.rs b/crates/ely_app/src/services/servo_live_wire.rs deleted file mode 100644 index 226b8a4..0000000 --- a/crates/ely_app/src/services/servo_live_wire.rs +++ /dev/null @@ -1,189 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::ServoLiveSitePermission; - -#[derive(Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub(super) enum LiveRequest { - Ensure { - tab_id: String, - profile_id: String, - url: String, - width: u32, - height: u32, - page_zoom_percent: u16, - device_pixel_ratio: f32, - scroll_delta_x: i32, - scroll_delta_y: i32, - scroll_point_x: Option, - scroll_point_y: Option, - click_x: Option, - click_y: Option, - hover_x: Option, - hover_y: Option, - typed_text: Option, - site_permissions: Vec, - ready_surface_ids: Vec, - }, - Poll { - tab_id: String, - ready_surface_ids: Vec, - }, - Close { - tab_id: String, - }, -} - -#[derive(Deserialize)] -pub(super) struct LiveResponse { - pub(super) error: Option, - pub(super) frame: Option, - #[serde(default)] - pub(super) perf: Option, - /// Hardware path only: present on the first frame after a new - /// IOSurface is bound (initial paint, resize, surfman swap chain - /// rotation). T10.4 will turn this into an imported Metal texture; - /// for now we log it on the `ely::servo::iosurface` target so the - /// pipeline is observable end-to-end without yet wiring it into - /// the renderer. - #[serde(default)] - pub(super) surface_handle: Option, - /// Hardware path only: which previously-imported IOSurface to - /// sample this frame. surfman's attached swap chain rotates the - /// bound surface, so this id alternates between the values the - /// receiver has already imported via `surface_handle`. - #[serde(default)] - pub(super) current_surface_id: Option, -} - -/// Wire mirror of `ely_servo_host::IOSurfaceHandle`. Duplicated rather -/// than imported because `ely_app` only talks to the sidecar via -/// stdin/stdout JSON — it has no crate dependency on `ely_servo_host` -/// and adding one just to share a four-field struct would pull the -/// Servo dep tree into the renderer process. -#[derive(Clone, Copy, Debug, Deserialize)] -pub(super) struct LiveSurfaceHandle { - pub(super) mach_port_name: u32, - pub(super) surface_id: u64, - pub(super) width: u32, - pub(super) height: u32, -} - -/// Aggregated frame-stage timings rolled up every N frames by the -/// sidecar. We accept anything matching the wire shape and let the -/// `tracing` event echo the percentiles verbatim — the sidecar is -/// the source of truth for histogram boundaries. -#[derive(Deserialize)] -pub(super) struct LiveFramePerfSummary { - window: u32, - context: String, - paint_p50_us: u64, - paint_p95_us: u64, - paint_p99_us: u64, - encode_p50_us: u64, - encode_p95_us: u64, - encode_p99_us: u64, - write_p50_us: u64, - write_p95_us: u64, - write_p99_us: u64, - total_p50_us: u64, - total_p95_us: u64, - total_p99_us: u64, -} - -#[derive(Deserialize)] -pub(super) struct LiveFrameReport { - pub(super) loaded_url: Option, - pub(super) title: Option, - pub(super) state: String, - pub(super) width: u32, - pub(super) height: u32, - #[serde(default = "default_device_pixel_ratio")] - pub(super) device_pixel_ratio: f32, - #[serde(default)] - pub(super) css_viewport_width: u32, - #[serde(default)] - pub(super) css_viewport_height: u32, - pub(super) rgba_byte_count: usize, - #[cfg(all(test, feature = "live-site-smoke"))] - pub(super) non_white_pixel_count: u64, - #[cfg(all(test, feature = "live-site-smoke"))] - pub(super) content_pixel_count: u64, - #[cfg(all(test, feature = "live-site-smoke"))] - pub(super) sample_hash: u64, -} - -fn default_device_pixel_ratio() -> f32 { - 1.0 -} - -/// Per-frame tag that tells the renderer which already-imported -/// `MTLTexture` to sample. Emitted at `trace` instead of `info` because -/// it fires every frame on the hardware path; the import event above -/// is the rare `info` and this trace is the steady-state breadcrumb. -pub(super) fn log_iosurface_current(surface_id: u64) { - tracing::trace!( - target: "ely::servo::iosurface", - surface_id, - "iosurface_current", - ); -} - -/// Emit one structured `tracing` event per IOSurface handover, on a -/// dedicated target so `RUST_LOG=ely::servo::iosurface=info` lights up -/// the cross-process surface pipeline without pulling in everything -/// else. The renderer (T10.4) will turn the same handle into an -/// imported Metal texture; today the event is the observable contract -/// that T10.3 plumbing is alive. -pub(super) fn log_iosurface_handle(handle: &LiveSurfaceHandle) { - tracing::info!( - target: "ely::servo::iosurface", - mach_port_name = handle.mach_port_name, - surface_id = handle.surface_id, - width = handle.width, - height = handle.height, - "iosurface_handle", - ); -} - -/// Emit one structured `tracing` event per perf summary, on a -/// dedicated target so `RUST_LOG=ely::servo::perf=info` flips the -/// stream on without dragging the rest of the app along. Filtering -/// happens upstream in the subscriber — this call is a single -/// pointer + integer push. -pub(super) fn log_frame_perf(summary: &LiveFramePerfSummary) { - tracing::info!( - target: "ely::servo::perf", - window = summary.window, - context = %summary.context, - paint_p50_us = summary.paint_p50_us, - paint_p95_us = summary.paint_p95_us, - paint_p99_us = summary.paint_p99_us, - encode_p50_us = summary.encode_p50_us, - encode_p95_us = summary.encode_p95_us, - encode_p99_us = summary.encode_p99_us, - write_p50_us = summary.write_p50_us, - write_p95_us = summary.write_p95_us, - write_p99_us = summary.write_p99_us, - total_p50_us = summary.total_p50_us, - total_p95_us = summary.total_p95_us, - total_p99_us = summary.total_p99_us, - "frame_perf", - ); -} - -#[cfg(test)] -mod tests { - use serde_json::json; - - use super::*; - - #[test] - fn close_request_serializes_to_wire() -> Result<(), serde_json::Error> { - let value = - serde_json::to_value(LiveRequest::Close { tab_id: "tab-live-close".to_string() })?; - - assert_eq!(value, json!({"type": "close", "tab_id": "tab-live-close"})); - Ok(()) - } -} diff --git a/crates/ely_app/src/services/servo_sidecar_command.rs b/crates/ely_app/src/services/servo_sidecar_command.rs deleted file mode 100644 index 4005335..0000000 --- a/crates/ely_app/src/services/servo_sidecar_command.rs +++ /dev/null @@ -1,220 +0,0 @@ -use std::{ - env, io, - path::{Path, PathBuf}, - process::Command, -}; - -use thiserror::Error; - -const SIDECAR_PATH_ENV: &str = "ELY_SERVO_SIDECAR"; -const RENDERING_CONTEXT_ENV: &str = "ELY_SERVO_RENDERING_CONTEXT"; -const SOFTWARE_SIDECAR_FEATURES: &str = "servo-engine"; -const HARDWARE_SIDECAR_FEATURES: &str = "servo-engine,hardware-render"; - -#[derive(Clone, Debug)] -pub(super) enum SidecarCommandTarget { - Binary(PathBuf), - Cargo { manifest_path: PathBuf }, -} - -impl SidecarCommandTarget { - pub(super) fn command(&self) -> Command { - match self { - Self::Binary(path) => Command::new(path), - Self::Cargo { manifest_path } => { - let mut command = Command::new("cargo"); - command - .arg("run") - .arg("--quiet") - .arg("--manifest-path") - .arg(manifest_path) - .arg("-p") - .arg("ely_servo_host") - .arg("--features") - .arg(sidecar_features_from_env()) - .arg("--bin") - .arg("ely_servo_sidecar") - .arg("--"); - command - } - } - } - - pub(super) fn missing_binary_path(&self) -> Option<&Path> { - match self { - Self::Binary(path) if !path.is_file() => Some(path.as_path()), - Self::Binary(_) | Self::Cargo { .. } => None, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(super) enum SidecarRenderingContext { - Software, - Hardware, -} - -impl SidecarRenderingContext { - pub(super) fn cli_arg(self) -> &'static str { - match self { - Self::Software => "software", - Self::Hardware => "hardware", - } - } - - fn sidecar_features(self) -> &'static str { - match self { - Self::Software => SOFTWARE_SIDECAR_FEATURES, - Self::Hardware => HARDWARE_SIDECAR_FEATURES, - } - } -} - -#[derive(Debug, Error)] -pub(crate) enum SidecarCommandError { - #[error("current executable path is unavailable: {0}")] - CurrentExecutable(#[source] io::Error), - - #[error("current executable directory is unavailable for {path}")] - CurrentExecutableDirectoryUnavailable { path: PathBuf }, -} - -pub(super) fn default_sidecar_command() -> Result { - if let Some(path) = env::var_os(SIDECAR_PATH_ENV) { - return Ok(SidecarCommandTarget::Binary(PathBuf::from(path))); - } - - let current_exe = env::current_exe().map_err(SidecarCommandError::CurrentExecutable)?; - let exe_dir = current_exe.parent().ok_or_else(|| { - SidecarCommandError::CurrentExecutableDirectoryUnavailable { path: current_exe.clone() } - })?; - let adjacent_sidecar = exe_dir.join(sidecar_binary_name()); - if adjacent_sidecar.is_file() && is_macos_app_bundle_exe_dir(exe_dir) { - return Ok(SidecarCommandTarget::Binary(adjacent_sidecar)); - } - let workspace_manifest = workspace_manifest_path(); - let workspace_target_sidecar = - workspace_manifest.as_ref().and_then(|path| workspace_target_sidecar_path(path)); - let adjacent_is_workspace_target = - workspace_target_sidecar.as_ref().is_some_and(|path| path == &adjacent_sidecar); - let workspace_target_sidecar_exists = - workspace_target_sidecar.as_ref().is_some_and(|path| path.is_file()); - let prefer_cargo_hardware_sidecar = rendering_context_from_env() - == SidecarRenderingContext::Hardware - && (adjacent_is_workspace_target || workspace_target_sidecar_exists) - && workspace_manifest.as_ref().is_some_and(|path| path.is_file()); - if adjacent_sidecar.is_file() && !prefer_cargo_hardware_sidecar { - return Ok(SidecarCommandTarget::Binary(adjacent_sidecar)); - } - - if let Some(manifest_path) = workspace_manifest { - if let Some(target_sidecar) = workspace_target_sidecar - && target_sidecar.is_file() - && !prefer_cargo_hardware_sidecar - { - return Ok(SidecarCommandTarget::Binary(target_sidecar)); - } - if manifest_path.is_file() { - return Ok(SidecarCommandTarget::Cargo { manifest_path }); - } - } - - Ok(SidecarCommandTarget::Binary(adjacent_sidecar)) -} - -fn workspace_manifest_path() -> Option { - option_env!("ELY_WORKSPACE_MANIFEST").map(PathBuf::from) -} - -pub(super) fn rendering_context_from_env() -> SidecarRenderingContext { - let raw = env::var(RENDERING_CONTEXT_ENV).ok(); - rendering_context_selection(raw.as_deref()) -} - -fn sidecar_features_from_env() -> &'static str { - rendering_context_from_env().sidecar_features() -} - -fn rendering_context_selection(raw: Option<&str>) -> SidecarRenderingContext { - match raw.map(str::to_lowercase).as_deref() { - Some("software") => SidecarRenderingContext::Software, - Some("hardware") => SidecarRenderingContext::Hardware, - _ => default_rendering_context(), - } -} - -fn default_rendering_context() -> SidecarRenderingContext { - if cfg!(target_os = "macos") { - SidecarRenderingContext::Hardware - } else { - SidecarRenderingContext::Software - } -} - -fn workspace_target_sidecar_path(manifest_path: &Path) -> Option { - let profile = if cfg!(debug_assertions) { "debug" } else { "release" }; - Some(manifest_path.parent()?.join("target").join(profile).join(sidecar_binary_name())) -} - -fn sidecar_binary_name() -> String { - format!("ely_servo_sidecar{}", env::consts::EXE_SUFFIX) -} - -fn is_macos_app_bundle_exe_dir(path: &Path) -> bool { - path.file_name().is_some_and(|name| name == "MacOS") - && path - .parent() - .is_some_and(|contents| contents.file_name().is_some_and(|name| name == "Contents")) - && path - .parent() - .and_then(Path::parent) - .is_some_and(|bundle| bundle.extension().is_some_and(|extension| extension == "app")) -} - -#[cfg(test)] -mod tests { - use super::{ - HARDWARE_SIDECAR_FEATURES, SOFTWARE_SIDECAR_FEATURES, SidecarRenderingContext, - is_macos_app_bundle_exe_dir, rendering_context_selection, - }; - - #[test] - fn hardware_rendering_context_enables_hardware_sidecar_feature() { - let context = rendering_context_selection(Some("hardware")); - assert_eq!(context, SidecarRenderingContext::Hardware); - assert_eq!(context.sidecar_features(), HARDWARE_SIDECAR_FEATURES); - assert_eq!( - rendering_context_selection(Some("HARDWARE")), - SidecarRenderingContext::Hardware - ); - } - - #[test] - fn software_rendering_context_uses_software_sidecar_feature() { - let context = rendering_context_selection(Some("software")); - assert_eq!(context, SidecarRenderingContext::Software); - assert_eq!(context.sidecar_features(), SOFTWARE_SIDECAR_FEATURES); - } - - #[test] - fn recognizes_macos_app_bundle_executable_directory() { - assert!(is_macos_app_bundle_exe_dir(std::path::Path::new( - "/tmp/ELY Browser.app/Contents/MacOS" - ))); - assert!(!is_macos_app_bundle_exe_dir(std::path::Path::new("/tmp/target/debug"))); - } - - #[cfg(target_os = "macos")] - #[test] - fn defaults_to_hardware_rendering_context_on_macos() { - assert_eq!(rendering_context_selection(None), SidecarRenderingContext::Hardware); - assert_eq!(rendering_context_selection(Some("garbage")), SidecarRenderingContext::Hardware); - } - - #[cfg(not(target_os = "macos"))] - #[test] - fn defaults_to_software_rendering_context_off_macos() { - assert_eq!(rendering_context_selection(None), SidecarRenderingContext::Software); - assert_eq!(rendering_context_selection(Some("garbage")), SidecarRenderingContext::Software); - } -} diff --git a/crates/ely_app/src/shell/gpui_harness_tests.rs b/crates/ely_app/src/shell/gpui_harness_tests.rs index a2c6ace..579f918 100644 --- a/crates/ely_app/src/shell/gpui_harness_tests.rs +++ b/crates/ely_app/src/shell/gpui_harness_tests.rs @@ -1,9 +1,9 @@ //! GPUI test harness for the input pipeline. //! -//! Twelve sidecar-side commits and one shell-side commit had all claimed to +//! Twelve renderer-side commits and one shell-side commit had all claimed to //! fix "click does nothing" while the user kept reporting the same symptom. //! The roundtable consensus: every store-layer test passed GREEN, every -//! sidecar integration test passed GREEN, but nothing in the repo exercised +//! renderer integration test passed GREEN, but nothing in the repo exercised //! the real GPUI event tree (`render_input_overlay` + window-level mouse //! handlers + sidebar capture interactions). This module is that missing //! holdout set. @@ -984,8 +984,7 @@ async fn baseline_overlay_div_receives_simulated_click(cx: &mut TestAppContext) /// **byte-identical RGBA payloads must produce the same underlying /// `Arc`**. Today they do not — every `from_live_frame` /// blindly reallocates. The fix path is either dedup the upload -/// against the last bytes or switch to `OffscreenRenderingContext` + -/// IOSurface so the GPU texture is the source of truth. +/// against the last bytes or switch to direct platform-surface presentation. /// /// Regression guard: with the single-slot `LAST_FRAME_IMAGE` cache in /// `web_surface_frame.rs`, two `ServoLiveFrame` inputs carrying @@ -1028,7 +1027,7 @@ fn identical_live_frames_share_render_image_arc() -> Result<(), String> { distinct Arc instances (first={:p}, second={:p}). \ WebSurfaceFrame::from_parts must dedup the upload against the \ previous frame's bytes, or the rendering pipeline must switch \ - to a GPU-side source of truth (IOSurface) so per-frame host \ + to direct platform-surface presentation so per-frame host \ allocations stop entirely.", Arc::as_ptr(first_image), Arc::as_ptr(second_image), diff --git a/crates/ely_app/src/shell/web_surface.rs b/crates/ely_app/src/shell/web_surface.rs index 0d72346..c674b2f 100644 --- a/crates/ely_app/src/shell/web_surface.rs +++ b/crates/ely_app/src/shell/web_surface.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::time::{Duration, Instant}; use ely_domain::{BrowserTab, TabId}; -use gpui::{Bounds, Pixels, Point}; +use gpui::{Bounds, NativeSurfaceHandle, Pixels, Point}; use crate::services::ProfileDataMode; @@ -56,8 +56,22 @@ impl WebSurfaceStore { else { return false; }; - let ensure_key = - WebSurfaceEnsureKey::new(requested_url.clone(), size, tab.zoom_percent(), permissions); + let native_surface = + self.surfaces.get(tab.id()).and_then(|surface| surface.native_surface.clone()); + #[cfg(not(test))] + let Some(native_surface) = native_surface else { + return false; + }; + let ensure_key = WebSurfaceEnsureKey::new( + requested_url.clone(), + size, + #[cfg(test)] + native_surface.as_ref(), + #[cfg(not(test))] + Some(&native_surface), + tab.zoom_percent(), + permissions, + ); if self.surfaces.get(tab.id()).is_some_and(|surface| !surface.should_ensure(&ensure_key)) { return false; } @@ -65,7 +79,29 @@ impl WebSurfaceStore { let previous_frame = self.previous_ready_frame(tab.id(), requested_url.as_str(), tab.zoom_percent()); - match self.runtime.ensure_tab(tab, size, profile_data_mode, permissions, input) { + #[cfg(test)] + let ensure_result = match native_surface { + Some(native_surface) => self.runtime.ensure_tab_with_native_surface( + tab, + size, + native_surface, + profile_data_mode, + permissions, + input, + ), + None => self.runtime.ensure_tab(tab, size, profile_data_mode, permissions, input), + }; + #[cfg(not(test))] + let ensure_result = self.runtime.ensure_tab_with_native_surface( + tab, + size, + native_surface, + profile_data_mode, + permissions, + input, + ); + + match ensure_result { Ok(result) => { self.surface_mut(tab.id()).mark_ensured(ensure_key); if result.started_loading { @@ -241,6 +277,24 @@ impl WebSurfaceStore { WebSurfaceInputOutcome::Applied } + pub(super) fn record_native_surface( + &mut self, + tab_id: &TabId, + native_surface: NativeSurfaceHandle, + ) -> WebSurfaceInputOutcome { + let surface = self.surface_mut(tab_id); + if surface + .native_surface + .as_ref() + .is_some_and(|current| current.identity() == native_surface.identity()) + { + return WebSurfaceInputOutcome::NoChange; + } + surface.native_surface = Some(native_surface); + surface.last_ensure_key = None; + WebSurfaceInputOutcome::Applied + } + pub(super) fn record_hover_point( &mut self, tab_id: &TabId, diff --git a/crates/ely_app/src/shell/web_surface_controller.rs b/crates/ely_app/src/shell/web_surface_controller.rs index 71f9c79..9a53371 100644 --- a/crates/ely_app/src/shell/web_surface_controller.rs +++ b/crates/ely_app/src/shell/web_surface_controller.rs @@ -1,6 +1,6 @@ use ely_browser_core::{BrowserCore, BrowserSnapshot}; use ely_domain::{BrowserTab, ProfileKind, TabId, UrlText}; -use gpui::{AnyElement, Bounds, Context, Pixels, Point}; +use gpui::{AnyElement, Bounds, Context, NativeSurfaceHandle, Pixels, Point}; use crate::services::ProfileDataMode; @@ -101,6 +101,25 @@ impl ElyShell { } } + pub(super) fn record_external_web_surface( + &mut self, + tab_id: TabId, + bounds: Bounds, + scale_factor: f32, + native_surface: NativeSurfaceHandle, + cx: &mut Context, + ) { + let viewport_changed = + self.web_surfaces.record_viewport_size(&tab_id, bounds, scale_factor) + == WebSurfaceInputOutcome::Applied; + let surface_changed = self.web_surfaces.record_native_surface(&tab_id, native_surface) + == WebSurfaceInputOutcome::Applied; + + if viewport_changed || surface_changed { + self.flush_external_web_surface_tick(cx); + } + } + pub(super) fn scroll_external_web_viewport( &mut self, tab_id: TabId, diff --git a/crates/ely_app/src/shell/web_surface_frame.rs b/crates/ely_app/src/shell/web_surface_frame.rs index ebe8d11..8af9a3d 100644 --- a/crates/ely_app/src/shell/web_surface_frame.rs +++ b/crates/ely_app/src/shell/web_surface_frame.rs @@ -3,10 +3,6 @@ use std::hash::Hasher; use std::sync::Arc; use ahash::AHasher; -#[cfg(target_os = "macos")] -use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_32BGRA}; -#[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] -use core_video::{pixel_buffer::kCVPixelBufferLock_ReadOnly, r#return::kCVReturnSuccess}; use gpui::RenderImage; use image::{ImageBuffer, Rgba}; use thiserror::Error; @@ -47,11 +43,7 @@ pub(super) struct WebSurfaceFrame { content_pixel_count: u64, #[cfg(all(test, feature = "live-site-smoke"))] sample_hash: u64, - /// Software-path image built from RGBA bytes. pub(super) image: Option>, - /// Hardware-path IOSurface imported from the sidecar. - #[cfg(target_os = "macos")] - pub(super) pixel_buffer: Option, } impl WebSurfaceFrame { @@ -61,8 +53,6 @@ impl WebSurfaceFrame { zoom_percent: u16, frame: ServoLiveFrame, ) -> Result { - #[cfg(target_os = "macos")] - let pixel_buffer = frame.pixel_buffer().cloned(); Self::from_parts(WebSurfaceFrameParts { requested_url, loaded_url: frame.loaded_url().map(str::to_string), @@ -84,45 +74,24 @@ impl WebSurfaceFrame { #[cfg(all(test, feature = "live-site-smoke"))] sample_hash: frame.sample_hash(), rgba_bytes: frame.into_rgba_bytes(), - #[cfg(target_os = "macos")] - pixel_buffer, }) } fn from_parts(parts: WebSurfaceFrameParts) -> Result { - #[cfg(target_os = "macos")] - let has_pixel_buffer = parts.pixel_buffer.is_some(); - #[cfg(not(target_os = "macos"))] - let has_pixel_buffer = false; - - if parts.rgba_bytes.is_empty() && !has_pixel_buffer { - return Err(WebSurfaceError::MissingRenderablePayload); - } - - #[cfg(target_os = "macos")] - if let Some(pixel_buffer) = parts.pixel_buffer.as_ref() { - validate_hardware_pixel_buffer(pixel_buffer, parts.width, parts.height)?; - } - #[cfg(all(test, feature = "live-site-smoke"))] let pixel_sample = pixel_sample_for_parts(&parts)?; - let image = if parts.rgba_bytes.is_empty() { - None - } else { - // Servo's `read_pixels(gl::RGBA, gl::UNSIGNED_BYTE)` writes - // R-G-B-A in memory order. GPUI's `RenderImage` is documented - // as "in BGRA format" and uploads via - // `MTLPixelFormat::BGRA8Unorm`, which reads B-G-R-A. Hand the bytes across - // unchanged and the Metal sampler treats R as B (and vice - // versa) — every coloured pixel renders with R and B swapped. - // Swap once here so the rest of the pipeline (dedup hash, - // image buffer, GPU upload) all operate on the same BGRA - // representation. - let mut bytes = parts.rgba_bytes; - swap_red_blue_in_place(&mut bytes); - let bytes_hash = rgba_hash(&bytes); - Some(resolve_render_image(parts.width, parts.height, bytes, bytes_hash)?) + let image = match parts.rgba_bytes { + Some(mut bytes) => { + if bytes.is_empty() { + return Err(WebSurfaceError::MissingRenderablePayload); + } + // Servo's RGBA8 readback is converted once for GPUI's BGRA upload path. + swap_red_blue_in_place(&mut bytes); + let bytes_hash = rgba_hash(&bytes); + Some(resolve_render_image(parts.width, parts.height, bytes, bytes_hash)?) + } + None => None, }; Ok(Self { @@ -146,8 +115,6 @@ impl WebSurfaceFrame { #[cfg(all(test, feature = "live-site-smoke"))] sample_hash: pixel_sample.sample_hash, image, - #[cfg(target_os = "macos")] - pixel_buffer: parts.pixel_buffer, }) } @@ -214,15 +181,13 @@ impl WebSurfaceFrame { } pub(super) fn has_same_software_render_as(&self, other: &Self) -> bool { - #[cfg(target_os = "macos")] - if self.pixel_buffer.is_some() || other.pixel_buffer.is_some() { - return false; - } - let (Some(image), Some(other_image)) = (self.image.as_ref(), other.image.as_ref()) else { - return false; + let image_matches = match (self.image.as_ref(), other.image.as_ref()) { + (Some(image), Some(other_image)) => Arc::ptr_eq(image, other_image), + (None, None) => true, + _ => false, }; - Arc::ptr_eq(image, other_image) + image_matches && self.requested_url == other.requested_url && self.loaded_url == other.loaded_url && self.title == other.title @@ -239,7 +204,6 @@ impl WebSurfaceFrame { } pub(super) fn has_visible_content_for_initial_display(&self) -> Result { - // Sidecar readback suppresses blank initial frames before publication. #[cfg(all(test, feature = "live-site-smoke"))] { Ok(self.non_white_pixel_count > 0 && self.content_pixel_count > 0) @@ -267,14 +231,7 @@ impl WebSurfaceFrame { #[cfg(all(test, feature = "live-site-smoke"))] pub(super) fn has_hardware_surface(&self) -> bool { - #[cfg(target_os = "macos")] - { - self.pixel_buffer.is_some() - } - #[cfg(not(target_os = "macos"))] - { - false - } + false } } @@ -298,67 +255,15 @@ struct WebSurfaceFrameParts { content_pixel_count: u64, #[cfg(all(test, feature = "live-site-smoke"))] sample_hash: u64, - rgba_bytes: Vec, - #[cfg(target_os = "macos")] - pixel_buffer: Option, + rgba_bytes: Option>, } #[derive(Debug, Error)] pub(super) enum WebSurfaceError { #[error("invalid servo frame buffer for {width}x{height}")] InvalidFrameBuffer { width: u32, height: u32 }, - #[error("servo live frame did not include a software image or hardware IOSurface")] + #[error("servo live frame did not include renderable pixels")] MissingRenderablePayload, - #[cfg(target_os = "macos")] - #[error( - "servo hardware surface size {actual_width}x{actual_height} did not match frame report {expected_width}x{expected_height}" - )] - HardwareSurfaceSizeMismatch { - expected_width: u32, - expected_height: u32, - actual_width: usize, - actual_height: usize, - }, - #[cfg(target_os = "macos")] - #[error("servo hardware surface pixel format 0x{actual:x} is unsupported; expected 32BGRA")] - UnsupportedHardwareSurfaceFormat { actual: u32 }, - #[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] - #[error("servo hardware surface lock failed with status {status}")] - HardwareSurfaceLockFailed { status: i32 }, - #[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] - #[error("servo hardware surface unlock failed with status {status}")] - HardwareSurfaceUnlockFailed { status: i32 }, - #[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] - #[error("servo hardware surface base address is unavailable")] - HardwareSurfaceBaseAddressUnavailable, - #[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] - #[error("servo hardware surface row stride {bytes_per_row} is too small for width {width}")] - HardwareSurfaceRowStrideTooSmall { width: usize, bytes_per_row: usize }, -} - -#[cfg(target_os = "macos")] -fn validate_hardware_pixel_buffer( - pixel_buffer: &CVPixelBuffer, - expected_width: u32, - expected_height: u32, -) -> Result<(), WebSurfaceError> { - let actual_width = pixel_buffer.get_width(); - let actual_height = pixel_buffer.get_height(); - if actual_width != expected_width as usize || actual_height != expected_height as usize { - return Err(WebSurfaceError::HardwareSurfaceSizeMismatch { - expected_width, - expected_height, - actual_width, - actual_height, - }); - } - - let actual_format = pixel_buffer.get_pixel_format(); - if actual_format != kCVPixelFormatType_32BGRA { - return Err(WebSurfaceError::UnsupportedHardwareSurfaceFormat { actual: actual_format }); - } - - Ok(()) } /// Swap byte 0 and byte 2 of every 4-byte pixel, converting Servo's @@ -387,11 +292,6 @@ struct WebSurfacePixelSample { fn pixel_sample_for_parts( parts: &WebSurfaceFrameParts, ) -> Result { - #[cfg(target_os = "macos")] - if let Some(pixel_buffer) = parts.pixel_buffer.as_ref() { - return sample_hardware_pixel_buffer(pixel_buffer); - } - Ok(WebSurfacePixelSample { non_white_pixel_count: parts.non_white_pixel_count, content_pixel_count: parts.content_pixel_count, @@ -399,82 +299,6 @@ fn pixel_sample_for_parts( }) } -#[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] -fn sample_hardware_pixel_buffer( - pixel_buffer: &CVPixelBuffer, -) -> Result { - let lock_status = pixel_buffer.lock_base_address(kCVPixelBufferLock_ReadOnly); - if lock_status != kCVReturnSuccess { - return Err(WebSurfaceError::HardwareSurfaceLockFailed { status: lock_status }); - } - - let sample = sample_locked_hardware_pixel_buffer(pixel_buffer); - let unlock_status = pixel_buffer.unlock_base_address(kCVPixelBufferLock_ReadOnly); - if unlock_status != kCVReturnSuccess { - return Err(WebSurfaceError::HardwareSurfaceUnlockFailed { status: unlock_status }); - } - - sample -} - -#[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] -fn sample_locked_hardware_pixel_buffer( - pixel_buffer: &CVPixelBuffer, -) -> Result { - let width = pixel_buffer.get_width(); - let height = pixel_buffer.get_height(); - let bytes_per_row = pixel_buffer.get_bytes_per_row(); - let row_width = width.saturating_mul(4); - if bytes_per_row < row_width { - return Err(WebSurfaceError::HardwareSurfaceRowStrideTooSmall { width, bytes_per_row }); - } - - #[expect(unsafe_code)] - let base_address = unsafe { pixel_buffer.get_base_address() }; - if base_address.is_null() { - return Err(WebSurfaceError::HardwareSurfaceBaseAddressUnavailable); - } - - let byte_len = bytes_per_row.saturating_mul(height); - #[expect(unsafe_code)] - let bytes = unsafe { std::slice::from_raw_parts(base_address.cast::(), byte_len) }; - Ok(sample_bgra_rows(bytes, width, height, bytes_per_row)) -} - -#[cfg(all(test, feature = "live-site-smoke", target_os = "macos"))] -fn sample_bgra_rows( - bytes: &[u8], - width: usize, - height: usize, - bytes_per_row: usize, -) -> WebSurfacePixelSample { - let mut non_white_pixel_count = 0; - let mut content_pixel_count = 0; - let mut sample_hash = 0xcbf29ce484222325_u64; - - for y in 0..height { - let row_start = y * bytes_per_row; - let row = &bytes[row_start..row_start + width * 4]; - for (x, pixel) in row.chunks_exact(4).enumerate() { - let [blue, green, red, alpha] = [pixel[0], pixel[1], pixel[2], pixel[3]]; - if alpha > 0 && (red < 245 || green < 245 || blue < 245) { - non_white_pixel_count += 1; - } - if alpha > 0 && (red < 220 || green < 220 || blue < 220) { - content_pixel_count += 1; - } - if (y * width + x).is_multiple_of(97) { - for byte in [red, green, blue, alpha] { - sample_hash ^= u64::from(byte); - sample_hash = sample_hash.wrapping_mul(0x100000001b3); - } - } - } - } - - WebSurfacePixelSample { non_white_pixel_count, content_pixel_count, sample_hash } -} - fn resolve_render_image( width: u32, height: u32, diff --git a/crates/ely_app/src/shell/web_surface_runtime.rs b/crates/ely_app/src/shell/web_surface_runtime.rs index abbe03d..23092ab 100644 --- a/crates/ely_app/src/shell/web_surface_runtime.rs +++ b/crates/ely_app/src/shell/web_surface_runtime.rs @@ -6,6 +6,7 @@ use std::{ }; use ely_domain::{BrowserTab, ProfileId, TabId}; +use gpui::NativeSurfaceHandle; use crate::services::{ ProfileDataMode, @@ -23,7 +24,9 @@ use super::{ }; pub(super) struct WebSurfaceRuntime { - workers: BTreeMap, + worker: Option, + direct_client: Option, + pending_direct_responses: Vec, sessions: BTreeMap, client_factory: LiveRuntimeClientFactory, } @@ -31,7 +34,9 @@ pub(super) struct WebSurfaceRuntime { impl WebSurfaceRuntime { pub(super) fn new() -> Self { Self { - workers: BTreeMap::new(), + worker: None, + direct_client: None, + pending_direct_responses: Vec::new(), sessions: BTreeMap::new(), client_factory: new_servo_live_client, } @@ -39,9 +44,16 @@ impl WebSurfaceRuntime { #[cfg(test)] pub(super) fn new_with_client_factory(client_factory: LiveRuntimeClientFactory) -> Self { - Self { workers: BTreeMap::new(), sessions: BTreeMap::new(), client_factory } + Self { + worker: None, + direct_client: None, + pending_direct_responses: Vec::new(), + sessions: BTreeMap::new(), + client_factory, + } } + #[cfg(test)] pub(super) fn ensure_tab( &mut self, tab: &BrowserTab, @@ -49,11 +61,48 @@ impl WebSurfaceRuntime { profile_data_mode: ProfileDataMode, permissions: &[WebSurfaceSitePermission], input: WebSurfacePendingInput, + ) -> Result { + self.ensure_tab_inner(tab, size, None, profile_data_mode, permissions, input) + } + + pub(super) fn ensure_tab_with_native_surface( + &mut self, + tab: &BrowserTab, + size: WebSurfaceSize, + native_surface: NativeSurfaceHandle, + profile_data_mode: ProfileDataMode, + permissions: &[WebSurfaceSitePermission], + input: WebSurfacePendingInput, + ) -> Result { + self.ensure_tab_inner( + tab, + size, + Some(native_surface), + profile_data_mode, + permissions, + input, + ) + } + + fn ensure_tab_inner( + &mut self, + tab: &BrowserTab, + size: WebSurfaceSize, + native_surface: Option, + profile_data_mode: ProfileDataMode, + permissions: &[WebSurfaceSitePermission], + input: WebSurfacePendingInput, ) -> Result { let scope = WebSurfaceRuntimeScope::new(tab.profile_id().clone(), profile_data_mode); - self.ensure_worker(scope.clone())?; + let use_direct_client = native_surface.is_some(); + if use_direct_client { + self.ensure_direct_client(scope.clone())?; + } else { + self.ensure_worker(scope.clone())?; + } let requested_url = tab.url().as_str().to_string(); + let tab_id_string = tab.id().as_str().to_string(); let zoom_percent = tab.zoom_percent(); let enqueued_at = input.enqueued_at; let input_kind = pending_input_kind(&input); @@ -88,6 +137,7 @@ impl WebSurfaceRuntime { height: size.height, page_zoom_percent: zoom_percent, device_pixel_ratio: size.device_pixel_ratio_f32(), + native_surface, scroll_delta_x, scroll_delta_y, scroll_point_x, @@ -100,10 +150,15 @@ impl WebSurfaceRuntime { site_permissions: permissions.iter().map(ServoLiveSitePermission::from).collect(), }; - let Some(scoped) = self.workers.get(&scope) else { - return Err("Servo worker was created but is no longer registered".to_string()); - }; - scoped.worker.submit_ensure(request); + if use_direct_client { + let response = self.ensure_direct(request, tab_id_string.clone())?; + self.pending_direct_responses.extend(response); + } else { + let Some(scoped) = self.worker.as_ref() else { + return Err("Servo worker was created but is no longer registered".to_string()); + }; + scoped.worker.submit_ensure(request); + } if let Some(session) = self.sessions.get_mut(tab.id()) { session.cadence.note_poll_submitted(submitted_at); } @@ -114,68 +169,19 @@ impl WebSurfaceRuntime { pub(super) fn tick(&mut self, visible_tab_ids: &[TabId]) -> Vec { let mut frames = Vec::new(); - let mut dead_scopes = Vec::new(); - let scopes: Vec = self.workers.keys().cloned().collect(); let now = Instant::now(); - for scope in scopes { - let responses = self - .workers - .get(&scope) - .map(|scoped| scoped.worker.drain_responses()) - .unwrap_or_default(); - for response in responses { - match response { - WorkerResponse::Frame { tab_id, frame } => { - let Some(tab_id_obj) = self.lookup_session_tab_id(&tab_id) else { - continue; - }; - let session = match self.sessions.get_mut(&tab_id_obj) { - Some(session) => session, - None => continue, - }; - let requested_url = session.requested_url.clone(); - let scroll_offset = session.scroll_offset; - let zoom_percent = session.zoom_percent; - session.cadence.note_frame(frame.render_state(), now); - match WebSurfaceFrame::from_live_frame( - requested_url.clone(), - scroll_offset, - zoom_percent, - frame, - ) { - Ok(frame) => { - let url_change = session.url_change_for( - &tab_id_obj, - requested_url.as_str(), - &frame, - ); - frames.push(WebSurfaceRuntimeFrame::Ready { - tab_id: tab_id_obj, - frame: Box::new(frame), - url_change, - }); - } - Err(error) => frames.push(WebSurfaceRuntimeFrame::Failed { - tab_id: tab_id_obj, - message: error.to_string(), - }), - } - } - WorkerResponse::Failed { tab_id, message } => { - let Some(tab_id_obj) = self.lookup_session_tab_id(&tab_id) else { - continue; - }; - frames.push(WebSurfaceRuntimeFrame::Failed { tab_id: tab_id_obj, message }); - } - WorkerResponse::SidecarExited => dead_scopes.push(scope.clone()), - } - } - } - for scope in dead_scopes { - self.workers.remove(&scope); + let mut responses = std::mem::take(&mut self.pending_direct_responses); + responses.extend( + self.worker.as_ref().map(|scoped| scoped.worker.drain_responses()).unwrap_or_default(), + ); + let runtime_unavailable = self.collect_responses(responses, now, &mut frames); + if runtime_unavailable { + self.remove_worker(); + self.remove_direct_client(); } let poll_now = Instant::now(); + let mut direct_polls = Vec::new(); for tab_id in visible_tab_ids { let Some(session) = self.sessions.get_mut(tab_id) else { continue; @@ -183,11 +189,22 @@ impl WebSurfaceRuntime { if !session.cadence.should_poll(poll_now) { continue; } - let Some(scoped) = self.workers.get(&session.scope) else { - continue; - }; - let _ = scoped.worker.submit_poll(tab_id.as_str().to_string()); - session.cadence.note_poll_submitted(poll_now); + if let Some(scoped) = self.worker.as_ref() { + let _ = scoped.worker.submit_poll(tab_id.as_str().to_string()); + session.cadence.note_poll_submitted(poll_now); + } else if self.direct_client.is_some() { + direct_polls.push(tab_id.as_str().to_string()); + session.cadence.note_poll_submitted(poll_now); + } + } + + if !direct_polls.is_empty() { + let (responses, runtime_unavailable) = self.poll_direct(direct_polls); + let unavailable_from_responses = + self.collect_responses(responses, Instant::now(), &mut frames); + if runtime_unavailable || unavailable_from_responses { + self.remove_direct_client(); + } } frames @@ -201,38 +218,156 @@ impl WebSurfaceRuntime { visible_tab_ids .iter() .filter_map(|tab_id| self.sessions.get(tab_id)) - .filter(|session| self.workers.contains_key(&session.scope)) + .filter(|_| self.worker.is_some() || self.direct_client.is_some()) .map(|session| session.cadence.next_poll_delay(now)) .min() } pub(super) fn close_tab(&mut self, tab_id: &TabId) { - let Some(session) = self.sessions.remove(tab_id) else { + if self.sessions.remove(tab_id).is_none() { return; - }; - if let Some(scoped) = self.workers.get(&session.scope) { + } + let direct_result = self + .direct_client + .as_mut() + .map(|scoped| scoped.client.close(tab_id.as_str().to_string())); + if direct_result.as_ref().is_some_and(|result| { + result.as_ref().is_err_and(|error| error.is_runtime_unavailable()) + }) { + self.remove_direct_client(); + } + if let Some(scoped) = self.worker.as_ref() { scoped.worker.submit_close(tab_id.as_str().to_string()); } } fn ensure_worker(&mut self, scope: WebSurfaceRuntimeScope) -> Result<(), String> { - if self.workers.contains_key(&scope) { + if self.worker.is_some() { + return Ok(()); + } + let (config_dir, transient_profile_data_dir) = config_dir_for_scope(&scope)?; + let client_factory = self.client_factory; + let worker = LiveRuntimeWorker::new(move || client_factory(config_dir))?; + self.worker = Some(ScopedWorker { worker, transient_profile_data_dir }); + Ok(()) + } + + fn ensure_direct_client(&mut self, scope: WebSurfaceRuntimeScope) -> Result<(), String> { + if self.direct_client.is_some() { return Ok(()); } let (config_dir, transient_profile_data_dir) = config_dir_for_scope(&scope)?; let client = (self.client_factory)(config_dir)?; - let worker = LiveRuntimeWorker::new(client)?; - self.workers.insert(scope, ScopedWorker { worker, transient_profile_data_dir }); + self.direct_client = Some(ScopedDirectClient { client, transient_profile_data_dir }); Ok(()) } + fn ensure_direct( + &mut self, + request: ServoLiveEnsureRequest, + tab_id: String, + ) -> Result, String> { + let Some(scoped) = self.direct_client.as_mut() else { + return Err("Servo client was created but is no longer registered".to_string()); + }; + match scoped.client.ensure(request) { + Ok(Some(frame)) => Ok(Some(WorkerResponse::Frame { tab_id, frame })), + Ok(None) => Ok(None), + Err(error) => { + let message = error.to_string(); + if error.is_runtime_unavailable() { + self.remove_direct_client(); + } + Err(message) + } + } + } + + fn poll_direct(&mut self, tab_ids: Vec) -> (Vec, bool) { + let Some(scoped) = self.direct_client.as_mut() else { + return (Vec::new(), false); + }; + let mut responses = Vec::new(); + let mut runtime_unavailable = false; + for tab_id in tab_ids { + match scoped.client.poll(tab_id.clone()) { + Ok(Some(frame)) => responses.push(WorkerResponse::Frame { tab_id, frame }), + Ok(None) => {} + Err(error) if error.is_runtime_unavailable() => { + runtime_unavailable = true; + responses.push(WorkerResponse::RuntimeUnavailable); + } + Err(error) => { + responses.push(WorkerResponse::Failed { tab_id, message: error.to_string() }) + } + } + } + (responses, runtime_unavailable) + } + + fn collect_responses( + &mut self, + responses: Vec, + now: Instant, + frames: &mut Vec, + ) -> bool { + let mut runtime_unavailable = false; + for response in responses { + match response { + WorkerResponse::Frame { tab_id, frame } => { + let Some(tab_id_obj) = self.lookup_session_tab_id(&tab_id) else { + continue; + }; + let session = match self.sessions.get_mut(&tab_id_obj) { + Some(session) => session, + None => continue, + }; + let requested_url = session.requested_url.clone(); + let scroll_offset = session.scroll_offset; + let zoom_percent = session.zoom_percent; + session.cadence.note_frame(frame.render_state(), now); + match WebSurfaceFrame::from_live_frame( + requested_url.clone(), + scroll_offset, + zoom_percent, + frame, + ) { + Ok(frame) => { + let url_change = + session.url_change_for(&tab_id_obj, requested_url.as_str(), &frame); + frames.push(WebSurfaceRuntimeFrame::Ready { + tab_id: tab_id_obj, + frame: Box::new(frame), + url_change, + }); + } + Err(error) => frames.push(WebSurfaceRuntimeFrame::Failed { + tab_id: tab_id_obj, + message: error.to_string(), + }), + } + } + WorkerResponse::Failed { tab_id, message } => { + let Some(tab_id_obj) = self.lookup_session_tab_id(&tab_id) else { + continue; + }; + frames.push(WebSurfaceRuntimeFrame::Failed { tab_id: tab_id_obj, message }); + } + WorkerResponse::RuntimeUnavailable => { + runtime_unavailable = true; + } + } + } + runtime_unavailable + } + fn lookup_session_tab_id(&self, tab_id: &str) -> Option { self.sessions.keys().find(|key| key.as_str() == tab_id).cloned() } #[cfg(test)] pub(super) fn client_count_for_test(&self) -> usize { - self.workers.len() + usize::from(self.worker.is_some()) + usize::from(self.direct_client.is_some()) } #[cfg(test)] @@ -242,23 +377,34 @@ impl WebSurfaceRuntime { #[cfg(test)] pub(super) fn flush_for_test(&self) { - for scoped in self.workers.values() { + if let Some(scoped) = self.worker.as_ref() { scoped.worker.wait_until_idle(); } } + + fn remove_worker(&mut self) { + let Some(scoped) = self.worker.take() else { + return; + }; + if let Some(path) = scoped.transient_profile_data_dir { + let _ = fs::remove_dir_all(path); + } + } + + fn remove_direct_client(&mut self) { + let Some(scoped) = self.direct_client.take() else { + return; + }; + if let Some(path) = scoped.transient_profile_data_dir { + let _ = fs::remove_dir_all(path); + } + } } impl Drop for WebSurfaceRuntime { fn drop(&mut self) { - let transient_profile_data_dirs = self - .workers - .values() - .filter_map(|scoped| scoped.transient_profile_data_dir.clone()) - .collect::>(); - self.workers.clear(); - for path in transient_profile_data_dirs { - let _ = fs::remove_dir_all(path); - } + self.remove_worker(); + self.remove_direct_client(); } } @@ -276,6 +422,11 @@ struct ScopedWorker { transient_profile_data_dir: Option, } +struct ScopedDirectClient { + client: Box, + transient_profile_data_dir: Option, +} + #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub(super) struct WebSurfaceRuntimeScope { profile_id: ProfileId, diff --git a/crates/ely_app/src/shell/web_surface_runtime_tests.rs b/crates/ely_app/src/shell/web_surface_runtime_tests.rs index eaad13b..b42b981 100644 --- a/crates/ely_app/src/shell/web_surface_runtime_tests.rs +++ b/crates/ely_app/src/shell/web_surface_runtime_tests.rs @@ -29,7 +29,7 @@ static FAILING_ENSURE_COUNT: AtomicUsize = AtomicUsize::new(0); static REPEATED_FRAME_ENSURE_COUNT: AtomicUsize = AtomicUsize::new(0); #[test] -fn runtime_keeps_independent_clients_for_profile_scopes() -> Result<(), String> { +fn runtime_shares_direct_servo_client_across_profile_scopes() -> Result<(), String> { let mut runtime = WebSurfaceRuntime::new_with_client_factory(fake_client_factory); let first_profile = ProfileId::new(); let second_profile = ProfileId::new(); @@ -60,7 +60,7 @@ fn runtime_keeps_independent_clients_for_profile_scopes() -> Result<(), String> runtime.flush_for_test(); - assert_eq!(runtime.client_count_for_test(), 2); + assert_eq!(runtime.client_count_for_test(), 1); assert_eq!( runtime.session_scope_for_test(first_tab.id()), Some(&WebSurfaceRuntimeScope::new(first_profile, ProfileDataMode::Transient)), @@ -183,7 +183,7 @@ fn identical_ready_software_frame_keeps_tick_unchanged() -> Result<(), String> { } #[test] -fn sidecar_exit_removes_dead_runtime_client() -> Result<(), String> { +fn runtime_unavailable_removes_dead_runtime_client() -> Result<(), String> { RECOVERY_FACTORY_COUNT.store(0, Ordering::SeqCst); let mut runtime = WebSurfaceRuntime::new_with_client_factory(recovery_client_factory); let profile = ProfileId::new(); @@ -281,7 +281,7 @@ fn session_scope_change_resets_tab_state() { struct FakeLiveRuntimeClient; struct IdleSkipLiveRuntimeClient; -struct SidecarExitLiveRuntimeClient; +struct RuntimeUnavailableLiveRuntimeClient; struct FailingLiveRuntimeClient; struct RepeatedFrameLiveRuntimeClient; @@ -322,20 +322,20 @@ impl LiveRuntimeClient for IdleSkipLiveRuntimeClient { } } -impl LiveRuntimeClient for SidecarExitLiveRuntimeClient { +impl LiveRuntimeClient for RuntimeUnavailableLiveRuntimeClient { fn ensure( &mut self, _request: ServoLiveEnsureRequest, ) -> Result, LiveRuntimeClientError> { - Err(LiveRuntimeClientError::SidecarExited) + Err(LiveRuntimeClientError::RuntimeUnavailable) } fn poll(&mut self, _tab_id: String) -> Result, LiveRuntimeClientError> { - Err(LiveRuntimeClientError::SidecarExited) + Err(LiveRuntimeClientError::RuntimeUnavailable) } fn close(&mut self, _tab_id: String) -> Result<(), LiveRuntimeClientError> { - Err(LiveRuntimeClientError::SidecarExited) + Err(LiveRuntimeClientError::RuntimeUnavailable) } } @@ -345,7 +345,7 @@ impl LiveRuntimeClient for FailingLiveRuntimeClient { _request: ServoLiveEnsureRequest, ) -> Result, LiveRuntimeClientError> { FAILING_ENSURE_COUNT.fetch_add(1, Ordering::SeqCst); - Err(LiveRuntimeClientError::SidecarExited) + Err(LiveRuntimeClientError::RuntimeUnavailable) } fn poll(&mut self, _tab_id: String) -> Result, LiveRuntimeClientError> { @@ -392,7 +392,7 @@ fn recovery_client_factory( ) -> Result, String> { let factory_call = RECOVERY_FACTORY_COUNT.fetch_add(1, Ordering::SeqCst); if factory_call == 0 { - return Ok(Box::new(SidecarExitLiveRuntimeClient)); + return Ok(Box::new(RuntimeUnavailableLiveRuntimeClient)); } Ok(Box::new(FakeLiveRuntimeClient)) } diff --git a/crates/ely_app/src/shell/web_surface_state.rs b/crates/ely_app/src/shell/web_surface_state.rs index 4f68ccc..f3fc3ed 100644 --- a/crates/ely_app/src/shell/web_surface_state.rs +++ b/crates/ely_app/src/shell/web_surface_state.rs @@ -1,7 +1,7 @@ use std::time::{Duration, Instant}; use ely_domain::TabId; -use gpui::{Bounds, Pixels}; +use gpui::{Bounds, NativeSurfaceHandle, Pixels}; use super::{ web_surface_cadence::ACTIVE_POLL_INTERVAL, @@ -126,6 +126,7 @@ pub(super) struct WebSurfaceTickResult { pub(super) struct PerTabSurface { pub(super) viewport_bounds: Option>, pub(super) viewport_size: Option, + pub(super) native_surface: Option, pub(super) last_ensure_key: Option, pub(super) hover_point: Option, last_hover_enqueued_at: Option, @@ -145,6 +146,7 @@ impl PerTabSurface { Self { viewport_bounds: None, viewport_size: None, + native_surface: None, last_ensure_key: None, hover_point: None, last_hover_enqueued_at: None, @@ -228,6 +230,7 @@ const HOVER_INPUT_MIN_INTERVAL: Duration = Duration::from_millis(32); pub(super) struct WebSurfaceEnsureKey { requested_url: String, size: WebSurfaceSize, + native_surface_id: Option, zoom_percent: u16, permissions: Vec, } @@ -236,10 +239,17 @@ impl WebSurfaceEnsureKey { pub(super) fn new( requested_url: String, size: WebSurfaceSize, + native_surface: Option<&NativeSurfaceHandle>, zoom_percent: u16, permissions: &[WebSurfaceSitePermission], ) -> Self { - Self { requested_url, size, zoom_percent, permissions: permissions.to_vec() } + Self { + requested_url, + size, + native_surface_id: native_surface.map(NativeSurfaceHandle::identity), + zoom_percent, + permissions: permissions.to_vec(), + } } } @@ -297,6 +307,7 @@ mod tests { WebSurfaceEnsureKey::new( url.to_string(), WebSurfaceSize { width, height, device_pixel_ratio_percent: 100 }, + None, 100, &[], ) diff --git a/crates/ely_app/src/shell/web_surface_tests.rs b/crates/ely_app/src/shell/web_surface_tests.rs index 698fc88..f6b7ebf 100644 --- a/crates/ely_app/src/shell/web_surface_tests.rs +++ b/crates/ely_app/src/shell/web_surface_tests.rs @@ -108,7 +108,7 @@ fn scroll_after_click_keeps_keyboard_focus_and_typed_text() -> Result<(), Box Result<(), Box Result<(), String> { Ok(_) => return Err("empty Servo frame payload reached Ready state".to_string()), Err(error) => error, }; - assert_eq!( - error.to_string(), - "servo live frame did not include a software image or hardware IOSurface", - ); - Ok(()) -} - -#[cfg(target_os = "macos")] -#[test] -fn hardware_live_frame_with_pixel_buffer_skips_software_image() -> Result<(), String> { - use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_32BGRA}; - - use crate::services::servo_live::ServoLiveFrame; - use crate::shell::web_surface_frame::WebSurfaceFrame; - use crate::shell::web_surface_geometry::WebSurfaceScrollOffset; - - let pixel_buffer = CVPixelBuffer::new(kCVPixelFormatType_32BGRA, 1, 1, None) - .map_err(|status| format!("CVPixelBufferCreate returned status {status}"))?; - let live = ServoLiveFrame::for_test_with_pixel_buffer(1, 1, pixel_buffer); - let frame = WebSurfaceFrame::from_live_frame( - "https://example.com/".to_string(), - WebSurfaceScrollOffset::default(), - 100, - live, - ) - .map_err(|error| error.to_string())?; - - assert!(frame.image.is_none(), "hardware frame should use the CVPixelBuffer surface path"); - assert!(frame.pixel_buffer.is_some(), "hardware frame should carry the imported CVPixelBuffer"); - Ok(()) -} - -#[cfg(target_os = "macos")] -#[test] -fn hardware_live_frame_rejects_mismatched_surface_size() -> Result<(), String> { - use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_32BGRA}; - - use crate::services::servo_live::ServoLiveFrame; - use crate::shell::web_surface_frame::WebSurfaceFrame; - use crate::shell::web_surface_geometry::WebSurfaceScrollOffset; - - let pixel_buffer = CVPixelBuffer::new(kCVPixelFormatType_32BGRA, 2, 1, None) - .map_err(|status| format!("CVPixelBufferCreate returned status {status}"))?; - let live = ServoLiveFrame::for_test_with_pixel_buffer(1, 1, pixel_buffer); - let result = WebSurfaceFrame::from_live_frame( - "https://example.com/".to_string(), - WebSurfaceScrollOffset::default(), - 100, - live, - ); - - let error = match result { - Ok(_) => return Err("mismatched hardware surface reached Ready state".to_string()), - Err(error) => error, - }; - assert_eq!(error.to_string(), "servo hardware surface size 2x1 did not match frame report 1x1",); - Ok(()) -} - -#[cfg(target_os = "macos")] -#[test] -fn hardware_live_frame_rejects_unsupported_surface_format() -> Result<(), String> { - use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange}; - - use crate::services::servo_live::ServoLiveFrame; - use crate::shell::web_surface_frame::WebSurfaceFrame; - use crate::shell::web_surface_geometry::WebSurfaceScrollOffset; - - let pixel_buffer = - CVPixelBuffer::new(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, 2, 2, None) - .map_err(|status| format!("CVPixelBufferCreate returned status {status}"))?; - let live = ServoLiveFrame::for_test_with_pixel_buffer(2, 2, pixel_buffer); - let result = WebSurfaceFrame::from_live_frame( - "https://example.com/".to_string(), - WebSurfaceScrollOffset::default(), - 100, - live, - ); - - let error = match result { - Ok(_) => return Err("unsupported hardware surface format reached Ready state".to_string()), - Err(error) => error, - }; - assert_eq!( - error.to_string(), - "servo hardware surface pixel format 0x34323066 is unsupported; expected 32BGRA", - ); - Ok(()) -} - -#[cfg(all(target_os = "macos", feature = "live-site-smoke"))] -#[test] -fn hardware_live_frame_samples_bgra_surface_pixels() -> Result<(), String> { - use core_video::{ - pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_32BGRA}, - r#return::kCVReturnSuccess, - }; - - use crate::services::servo_live::ServoLiveFrame; - use crate::shell::web_surface_frame::WebSurfaceFrame; - use crate::shell::web_surface_geometry::WebSurfaceScrollOffset; - - let pixel_buffer = CVPixelBuffer::new(kCVPixelFormatType_32BGRA, 2, 1, None) - .map_err(|status| format!("CVPixelBufferCreate returned status {status}"))?; - let lock_status = pixel_buffer.lock_base_address(0); - if lock_status != kCVReturnSuccess { - return Err(format!("CVPixelBufferLockBaseAddress returned status {lock_status}")); - } - let bytes_per_row = pixel_buffer.get_bytes_per_row(); - #[expect(unsafe_code)] - unsafe { - let base_address = pixel_buffer.get_base_address().cast::(); - let bytes = std::slice::from_raw_parts_mut(base_address, bytes_per_row); - bytes[0..8].copy_from_slice(&[ - 0, 0, 255, 255, // red in BGRA memory order - 255, 255, 255, 255, - ]); - } - let unlock_status = pixel_buffer.unlock_base_address(0); - if unlock_status != kCVReturnSuccess { - return Err(format!("CVPixelBufferUnlockBaseAddress returned status {unlock_status}")); - } - - let live = ServoLiveFrame::for_test_with_pixel_buffer(2, 1, pixel_buffer); - let frame = WebSurfaceFrame::from_live_frame( - "https://example.com/".to_string(), - WebSurfaceScrollOffset::default(), - 100, - live, - ) - .map_err(|error| error.to_string())?; - - assert_eq!(frame.non_white_pixel_count(), 1); - assert_eq!(frame.content_pixel_count(), 1); - assert_ne!(frame.sample_hash(), 0); + assert_eq!(error.to_string(), "servo live frame did not include renderable pixels",); Ok(()) } diff --git a/crates/ely_app/src/shell/web_surface_view.rs b/crates/ely_app/src/shell/web_surface_view.rs index 947595e..0c91e18 100644 --- a/crates/ely_app/src/shell/web_surface_view.rs +++ b/crates/ely_app/src/shell/web_surface_view.rs @@ -1,51 +1,28 @@ use ely_domain::{BrowserTab, TabId}; use gpui::{ - AnyElement, App, Corners, Entity, ImageSource, InteractiveElement, IntoElement, MouseButton, - ObjectFit, ParentElement, Styled, StyledImage, Window, canvas, div, img, px, rgb, surface, + AnyElement, App, ElementId, Entity, InteractiveElement, IntoElement, MouseButton, + ParentElement, Styled, Window, canvas, div, native_surface, px, rgb, }; use super::{ ElyShell, web_surface_frame::WebSurfaceFrame, web_surface_geometry::servo_scroll_delta_from_wheel_delta, }; -use ely_design_system::{colors, spacing}; +use ely_design_system::colors; pub(super) fn render_ready_web_surface( - frame: &WebSurfaceFrame, + _frame: &WebSurfaceFrame, tab: &BrowserTab, state_entity: Entity, ) -> AnyElement { - #[cfg(target_os = "macos")] - if let Some(pixel_buffer) = frame.pixel_buffer.as_ref() { - return render_web_surface( - tab, - state_entity, - surface(pixel_buffer.clone()) - .size_full() - .corner_radii(web_surface_corner_radii()) - .object_fit(ObjectFit::Fill), - ); - } - - if let Some(image) = frame.image.as_ref() { - return render_web_surface( - tab, - state_entity, - img(ImageSource::Render(image.clone())).size_full().object_fit(ObjectFit::Fill), - ); - } - render_web_surface( - tab, - state_entity, - error_page("Web surface frame did not include renderable pixels."), - ) + render_web_surface(tab, state_entity.clone(), render_native_web_surface(tab, state_entity)) } pub(super) fn render_loading_web_surface( tab: &BrowserTab, state_entity: Entity, ) -> AnyElement { - render_web_surface(tab, state_entity, div().size_full()) + render_web_surface(tab, state_entity.clone(), render_native_web_surface(tab, state_entity)) } pub(super) fn render_failed_web_surface( @@ -56,15 +33,6 @@ pub(super) fn render_failed_web_surface( render_web_surface(tab, state_entity, error_page(message)) } -fn web_surface_corner_radii() -> Corners { - Corners { - top_left: px(0.0), - top_right: px(0.0), - bottom_right: px(spacing::RADIUS_CARD), - bottom_left: px(spacing::RADIUS_CARD), - } -} - fn error_page(message: &str) -> impl IntoElement { div() .size_full() @@ -107,6 +75,18 @@ fn render_web_surface( .into_any_element() } +fn render_native_web_surface(tab: &BrowserTab, state_entity: Entity) -> impl IntoElement { + let tab_id = tab.id().clone(); + let element_id = ElementId::Name(format!("web-surface-{}", tab_id.as_str()).into()); + native_surface(element_id, move |surface, bounds, window: &mut Window, cx: &mut App| { + let scale_factor = window.scale_factor(); + state_entity.update(cx, |shell, cx| { + shell.record_external_web_surface(tab_id.clone(), bounds, scale_factor, surface, cx); + }); + }) + .size_full() +} + fn render_input_overlay( tab_id: TabId, url: String, diff --git a/crates/ely_app/src/shell/web_surface_worker.rs b/crates/ely_app/src/shell/web_surface_worker.rs index e66ef6c..26fa900 100644 --- a/crates/ely_app/src/shell/web_surface_worker.rs +++ b/crates/ely_app/src/shell/web_surface_worker.rs @@ -9,13 +9,12 @@ use crate::services::servo_live::{ ServoLiveClient, ServoLiveEnsureRequest, ServoLiveError, ServoLiveFrame, }; -/// IPC surface for the per-profile Servo sidecar. +/// Blocking surface for the embedded Servo runtime. /// /// Production wraps [`ServoLiveClient`] directly; tests substitute a /// fake. The contract: every call is blocking and may run for tens of -/// milliseconds. Implementations live on the worker thread, never the -/// UI thread. -pub(super) trait LiveRuntimeClient: Send { +/// milliseconds. Implementations live on the worker thread. +pub(super) trait LiveRuntimeClient { fn ensure( &mut self, request: ServoLiveEnsureRequest, @@ -45,20 +44,20 @@ impl LiveRuntimeClient for ServoLiveClient { #[derive(Debug)] pub(super) enum LiveRuntimeClientError { - SidecarExited, + RuntimeUnavailable, Message(String), } impl LiveRuntimeClientError { - pub(super) fn is_sidecar_exited(&self) -> bool { - matches!(self, Self::SidecarExited) + pub(super) fn is_runtime_unavailable(&self) -> bool { + matches!(self, Self::RuntimeUnavailable) } } impl std::fmt::Display for LiveRuntimeClientError { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::SidecarExited => formatter.write_str("servo live sidecar exited"), + Self::RuntimeUnavailable => formatter.write_str("servo live runtime is unavailable"), Self::Message(message) => formatter.write_str(message), } } @@ -72,8 +71,8 @@ impl From for LiveRuntimeClientError { impl From for LiveRuntimeClientError { fn from(error: ServoLiveError) -> Self { - if error.is_sidecar_process_unusable() { - return Self::SidecarExited; + if error.is_runtime_unavailable() { + return Self::RuntimeUnavailable; } Self::Message(error.to_string()) } @@ -89,7 +88,7 @@ impl From for LiveRuntimeClientError { pub(super) enum WorkerResponse { Frame { tab_id: String, frame: ServoLiveFrame }, Failed { tab_id: String, message: String }, - SidecarExited, + RuntimeUnavailable, } enum WorkerRequest { @@ -116,9 +115,9 @@ struct WorkerQueue { /// Owns a [`LiveRuntimeClient`] on a dedicated OS thread and exposes /// a non-blocking API: submit ensure/poll/close, then drain responses. /// -/// The UI thread never blocks on Servo IPC. Submissions push into a +/// The UI thread never blocks on Servo. Submissions push into a /// coalescing queue (latest request per tab wins). The worker thread -/// drains the queue, runs the blocking IPC, and emits responses on a +/// drains the queue, runs the blocking calls, and emits responses on a /// `std::sync::mpsc` channel that the UI thread reads with `try_recv`. pub(super) struct LiveRuntimeWorker { queue: Arc<(Mutex, Condvar)>, @@ -127,7 +126,9 @@ pub(super) struct LiveRuntimeWorker { } impl LiveRuntimeWorker { - pub(super) fn new(client: Box) -> Result { + pub(super) fn new( + client_factory: impl FnOnce() -> Result, String> + Send + 'static, + ) -> Result { let queue = Arc::new(( Mutex::new(WorkerQueue { pending: BTreeMap::new(), @@ -138,13 +139,35 @@ impl LiveRuntimeWorker { Condvar::new(), )); let (response_tx, response_rx) = mpsc::channel(); + let (init_tx, init_rx) = mpsc::channel(); let queue_for_thread = queue.clone(); let thread = std::thread::Builder::new() - .name("ely-servo-live".to_string()) + .name("ely-servo-runtime".to_string()) .spawn(move || { + let client = match client_factory() { + Ok(client) => { + let _ = init_tx.send(Ok(())); + client + } + Err(error) => { + let _ = init_tx.send(Err(error)); + return; + } + }; run_worker(client, queue_for_thread, response_tx); }) .map_err(|error| format!("failed to spawn servo live worker thread: {error}"))?; + match init_rx.recv() { + Ok(Ok(())) => {} + Ok(Err(error)) => { + let _ = thread.join(); + return Err(error); + } + Err(error) => { + let _ = thread.join(); + return Err(format!("servo live worker initialization failed: {error}")); + } + } Ok(Self { queue, response_rx, thread: Some(thread) }) } @@ -317,7 +340,7 @@ enum Work { } /// Forward a single client result to the response channel. Returns -/// `true` when the worker should exit (sidecar process died). +/// `true` when the worker should exit. fn dispatch_result( response_tx: &mpsc::Sender, tab_id: String, @@ -330,11 +353,11 @@ fn dispatch_result( } Ok(None) => false, Err(error) => { - let exited = error.is_sidecar_exited(); + let unavailable = error.is_runtime_unavailable(); let message = error.to_string(); let _ = response_tx.send(WorkerResponse::Failed { tab_id, message }); - if exited { - let _ = response_tx.send(WorkerResponse::SidecarExited); + if unavailable { + let _ = response_tx.send(WorkerResponse::RuntimeUnavailable); return true; } false diff --git a/crates/ely_servo_host/Cargo.toml b/crates/ely_servo_host/Cargo.toml index aaff3ab..91533fd 100644 --- a/crates/ely_servo_host/Cargo.toml +++ b/crates/ely_servo_host/Cargo.toml @@ -7,41 +7,30 @@ rust-version.workspace = true [features] default = [] -servo-engine = ["dep:dpi", "dep:euclid", "dep:serde", "dep:serde_json", "dep:servo", "dep:url"] -hardware-render = [ - "servo-engine", - "dep:gleam", - "dep:glow", - "dep:image", - "dep:log", - "dep:mach2", - "dep:surfman", - "dep:objc2-io-surface", +servo-engine = [ + "dep:dpi", + "dep:euclid", + "dep:naga", + "dep:raw-window-handle", + "dep:rustls", + "dep:serde", + "dep:serde_json", + "dep:servo", + "dep:url", ] -[[bin]] -name = "ely_servo_sidecar" -path = "src/bin/ely_servo_sidecar.rs" -required-features = ["servo-engine"] - [dependencies] dpi = { workspace = true, optional = true } ely_domain = { path = "../ely_domain" } euclid = { version = "0.22", optional = true } -gleam = { version = "0.15", optional = true } -glow = { version = "0.16", optional = true } -image = { workspace = true, optional = true } -log = { version = "0.4", optional = true } +naga = { version = "26.0.0", features = ["termcolor"], optional = true } +raw-window-handle = { version = "0.6", optional = true } +rustls = { version = "0.23.40", default-features = false, features = ["std", "aws_lc_rs"], optional = true } serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } servo = { workspace = true, optional = true } -surfman = { version = "0.11", optional = true } thiserror.workspace = true url = { workspace = true, optional = true } -[target.'cfg(target_os = "macos")'.dependencies] -mach2 = { version = "0.6", optional = true } -objc2-io-surface = { version = "0.3.2", optional = true } - [lints] workspace = true diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar.rs deleted file mode 100644 index 9916b63..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar.rs +++ /dev/null @@ -1,332 +0,0 @@ -use std::{ - io::Write, - thread, - time::{Duration, Instant}, -}; - -use ely_domain::TabId; -use ely_servo_host::{ - KeyboardTextRequest, MouseClickRequest, MouseDragRequest, NavigationRequest, PageZoomRequest, - PermissionRequest, ScrollRequest, ServoHost, ServoHostError, ServoSurfaceSize, - SoftwareServoHost, TouchTapRequest, WebViewSnapshot, WebViewState, -}; -use thiserror::Error; - -#[path = "ely_servo_sidecar/args.rs"] -mod args; -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -#[path = "ely_servo_sidecar/iosurface_mach.rs"] -mod iosurface_mach; -#[path = "ely_servo_sidecar/live.rs"] -mod live; -#[path = "ely_servo_sidecar/live_output.rs"] -mod live_output; -#[path = "ely_servo_sidecar/live_protocol.rs"] -mod live_protocol; -#[path = "ely_servo_sidecar/live_session.rs"] -mod live_session; -#[path = "ely_servo_sidecar/perf.rs"] -mod perf; -#[path = "ely_servo_sidecar/report.rs"] -mod report; - -use args::{SidecarCommand, SnapshotArgs}; -use report::{SnapshotInputChanges, SnapshotReport}; - -const WAIT_ITERATIONS: usize = 5_000; -const WAIT_INTERVAL: Duration = Duration::from_millis(2); -const RENDER_TIMEOUT: Duration = Duration::from_secs(20); -const VISIBLE_FRAME_SETTLE_TIMEOUT: Duration = Duration::from_millis(700); -const INPUT_SETTLE_TIMEOUT: Duration = Duration::from_millis(700); - -fn main() -> Result<(), SidecarError> { - match args::parse_env_command()? { - SidecarCommand::Live(args) => live::run_live(args).map_err(SidecarError::Live), - SidecarCommand::Snapshot(args) => run_snapshot(args), - } -} - -#[derive(Debug, Error)] -enum SidecarError { - #[error("timed out rendering {url}: {snapshot:?}")] - RenderTimeout { url: String, snapshot: Box }, - - #[error(transparent)] - Args(#[from] args::SidecarArgsError), - - #[error(transparent)] - Host(#[from] ServoHostError), - - #[error(transparent)] - Live(#[from] live::LiveSidecarError), - - #[error(transparent)] - Io(#[from] std::io::Error), - - #[error(transparent)] - Json(#[from] serde_json::Error), -} - -fn run_snapshot(args: SnapshotArgs) -> Result<(), SidecarError> { - std::fs::create_dir_all(&args.profile_data_dir)?; - let mut host = SoftwareServoHost::new_with_config_dir( - ServoSurfaceSize::new(args.width, args.height), - Some(args.profile_data_dir.clone()), - )?; - let tab_id = TabId::new(); - let webview_id = host.create_webview(tab_id.clone(), args.profile_id.clone())?; - apply_site_permissions(&mut host, &webview_id, &args)?; - host.set_page_zoom(PageZoomRequest { - webview_id: webview_id.clone(), - zoom_factor: f32::from(args.page_zoom_percent) / 100.0, - })?; - - host.navigate(NavigationRequest { - webview_id: webview_id.clone(), - tab_id, - url: args.url.clone(), - })?; - - let snapshot = wait_for_frame(&mut host, &webview_id, args.url.as_str())?; - let (snapshot, scroll_changed_frame) = - apply_scroll_if_requested(&mut host, &webview_id, &args, snapshot)?; - let (snapshot, click_changed_frame) = - apply_click_if_requested(&mut host, &webview_id, &args, snapshot)?; - let (snapshot, drag_changed_frame) = - apply_drag_if_requested(&mut host, &webview_id, &args, snapshot)?; - let (snapshot, touch_changed_frame) = - apply_touch_if_requested(&mut host, &webview_id, &args, snapshot)?; - let (snapshot, text_changed_frame) = - apply_text_if_requested(&mut host, &webview_id, &args, snapshot)?; - let frame = host.last_rendered_frame()?; - std::fs::write(&args.rgba_out, frame.rgba_bytes())?; - - let mut stdout = std::io::stdout().lock(); - serde_json::to_writer( - &mut stdout, - &SnapshotReport::new( - &args, - &snapshot, - &frame, - SnapshotInputChanges { - scroll: scroll_changed_frame, - click: click_changed_frame, - drag: drag_changed_frame, - touch: touch_changed_frame, - text: text_changed_frame, - }, - ), - )?; - stdout.write_all(b"\n")?; - stdout.flush()?; - std::process::exit(0); -} - -fn apply_site_permissions( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - args: &SnapshotArgs, -) -> Result<(), SidecarError> { - for permission in &args.site_permissions { - host.set_permission( - PermissionRequest { - webview_id: webview_id.clone(), - profile_id: args.profile_id.clone(), - origin: permission.origin.clone(), - feature: permission.feature, - }, - permission.decision.into(), - )?; - } - - Ok(()) -} - -fn apply_scroll_if_requested( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - args: &SnapshotArgs, - snapshot: WebViewSnapshot, -) -> Result<(WebViewSnapshot, bool), SidecarError> { - if args.scroll_x == 0 && args.scroll_y == 0 { - return Ok((snapshot, false)); - } - - let previous_frame_hash = host.last_rendered_frame()?.sample_hash(); - host.scroll(ScrollRequest { - webview_id: webview_id.clone(), - delta_x: args.scroll_x, - delta_y: args.scroll_y, - point_x: 0, - point_y: 0, - })?; - wait_for_changed_or_settled_frame(host, webview_id, previous_frame_hash) -} - -fn apply_click_if_requested( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - args: &SnapshotArgs, - snapshot: WebViewSnapshot, -) -> Result<(WebViewSnapshot, bool), SidecarError> { - let Some(click_point) = args.click_point else { - return Ok((snapshot, false)); - }; - - let previous_frame_hash = host.last_rendered_frame()?.sample_hash(); - host.click(MouseClickRequest { - webview_id: webview_id.clone(), - x: click_point.x, - y: click_point.y, - })?; - wait_for_changed_or_settled_frame(host, webview_id, previous_frame_hash) -} - -fn apply_drag_if_requested( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - args: &SnapshotArgs, - snapshot: WebViewSnapshot, -) -> Result<(WebViewSnapshot, bool), SidecarError> { - let Some(drag_points) = args.drag_points else { - return Ok((snapshot, false)); - }; - - let previous_frame_hash = host.last_rendered_frame()?.sample_hash(); - host.drag(MouseDragRequest { - webview_id: webview_id.clone(), - from_x: drag_points.from.x, - from_y: drag_points.from.y, - to_x: drag_points.to.x, - to_y: drag_points.to.y, - })?; - wait_for_changed_or_settled_frame(host, webview_id, previous_frame_hash) -} - -fn apply_touch_if_requested( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - args: &SnapshotArgs, - snapshot: WebViewSnapshot, -) -> Result<(WebViewSnapshot, bool), SidecarError> { - let Some(touch_point) = args.touch_point else { - return Ok((snapshot, false)); - }; - - let previous_frame_hash = host.last_rendered_frame()?.sample_hash(); - host.touch_tap(TouchTapRequest { - webview_id: webview_id.clone(), - x: touch_point.x, - y: touch_point.y, - })?; - wait_for_changed_or_settled_frame(host, webview_id, previous_frame_hash) -} - -fn apply_text_if_requested( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - args: &SnapshotArgs, - snapshot: WebViewSnapshot, -) -> Result<(WebViewSnapshot, bool), SidecarError> { - let Some(typed_text) = args.typed_text.as_ref() else { - return Ok((snapshot, false)); - }; - - let previous_frame_hash = host.last_rendered_frame()?.sample_hash(); - host.type_text(KeyboardTextRequest { - webview_id: webview_id.clone(), - text: typed_text.clone(), - })?; - wait_for_changed_or_settled_frame(host, webview_id, previous_frame_hash) -} - -fn wait_for_frame( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - url: &str, -) -> Result { - let started_at = Instant::now(); - let mut latest_rendered_snapshot = None; - let mut visible_frame_hash = None; - let mut visible_frame_last_changed_at = None; - - for _ in 0..WAIT_ITERATIONS { - if started_at.elapsed() >= RENDER_TIMEOUT { - break; - } - - host.tick(); - let snapshot = host.snapshot(webview_id)?; - if snapshot.has_pending_frame() { - host.paint(webview_id)?; - } - - let snapshot = host.snapshot(webview_id)?; - if let Ok(frame) = host.last_rendered_frame() - && frame.non_white_pixel_count() > 0 - && frame.content_pixel_count() > 0 - { - let current_hash = frame.sample_hash(); - if visible_frame_hash != Some(current_hash) { - visible_frame_hash = Some(current_hash); - visible_frame_last_changed_at = Some(Instant::now()); - } - - if snapshot.state() == &WebViewState::Complete { - return Ok(snapshot); - } - - latest_rendered_snapshot = Some(snapshot.clone()); - if snapshot.url().is_some() - && visible_frame_last_changed_at - .is_some_and(|changed_at| changed_at.elapsed() >= VISIBLE_FRAME_SETTLE_TIMEOUT) - { - return Ok(snapshot); - } - } - - thread::sleep(WAIT_INTERVAL); - } - - if let Some(snapshot) = latest_rendered_snapshot { - return Ok(snapshot); - } - - Err(SidecarError::RenderTimeout { - url: url.to_string(), - snapshot: Box::new(host.snapshot(webview_id)?), - }) -} - -fn wait_for_changed_or_settled_frame( - host: &mut SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - previous_frame_hash: u64, -) -> Result<(WebViewSnapshot, bool), SidecarError> { - let started_at = Instant::now(); - let mut latest_snapshot = host.snapshot(webview_id)?; - - for _ in 0..WAIT_ITERATIONS { - if started_at.elapsed() >= INPUT_SETTLE_TIMEOUT { - break; - } - - host.tick(); - let snapshot = host.snapshot(webview_id)?; - if snapshot.has_pending_frame() { - host.paint(webview_id)?; - } - - latest_snapshot = host.snapshot(webview_id)?; - let changed_frame = host.last_rendered_frame().is_ok_and(|frame| { - frame.non_white_pixel_count() > 0 && frame.sample_hash() != previous_frame_hash - }); - if changed_frame { - return Ok((latest_snapshot, true)); - } - - thread::sleep(WAIT_INTERVAL); - } - - Ok((latest_snapshot, false)) -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/args.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/args.rs deleted file mode 100644 index 92ab9b3..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/args.rs +++ /dev/null @@ -1,395 +0,0 @@ -use std::{env, num::ParseIntError, path::PathBuf}; - -use ely_domain::{ - DEFAULT_ZOOM_PERCENT, ProfileId, SiteOrigin, SitePermissionDecision, SitePermissionFeature, - UrlText, validate_zoom_percent, -}; -use ely_servo_host::RenderingContextKind; -use serde::Deserialize; -use thiserror::Error; - -pub(super) enum SidecarCommand { - Live(LiveArgs), - Snapshot(SnapshotArgs), -} - -pub(super) struct LiveArgs { - pub(super) profile_data_dir: PathBuf, - pub(super) iosurface_mach_service: Option, - /// Rendering context the host's webviews are built against. - /// Defaults to [`RenderingContextKind::Software`], which keeps - /// the binary's behaviour bit-identical to pre-flag builds. - /// `Hardware` is only accepted when the `hardware-render` - /// feature is compiled in (otherwise the `SoftwareServoHost` - /// constructor returns `HardwareRenderUnavailable`). - pub(super) rendering_context_kind: RenderingContextKind, -} - -pub(super) struct SnapshotArgs { - pub(super) url: UrlText, - pub(super) profile_id: ProfileId, - pub(super) profile_data_dir: PathBuf, - pub(super) rgba_out: PathBuf, - pub(super) width: u32, - pub(super) height: u32, - pub(super) scroll_x: i32, - pub(super) scroll_y: i32, - pub(super) page_zoom_percent: u16, - pub(super) click_point: Option, - pub(super) drag_points: Option, - pub(super) touch_point: Option, - pub(super) typed_text: Option, - pub(super) site_permissions: Vec, -} - -#[derive(Clone, Copy)] -pub(super) struct ClickPoint { - pub(super) x: u32, - pub(super) y: u32, -} - -#[derive(Clone, Copy)] -pub(super) struct DragPoints { - pub(super) from: ClickPoint, - pub(super) to: ClickPoint, -} - -pub(super) struct SidecarSitePermission { - pub(super) origin: SiteOrigin, - pub(super) feature: SitePermissionFeature, - pub(super) decision: SitePermissionDecision, -} - -#[derive(Debug, Error)] -pub(super) enum SidecarArgsError { - #[error("missing sidecar command")] - MissingCommand, - - #[error("unknown sidecar command: {value}")] - UnknownCommand { value: String }, - - #[error("missing argument value for {name}")] - MissingArgumentValue { name: &'static str }, - - #[error("missing required argument: {name}")] - MissingRequiredArgument { name: &'static str }, - - #[error("unknown argument: {value}")] - UnknownArgument { value: String }, - - #[error("{name} must be an integer: {value}")] - InvalidInteger { - name: &'static str, - value: String, - #[source] - source: ParseIntError, - }, - - #[error("{name} must be greater than zero")] - ZeroDimension { name: &'static str }, - - #[error("--click-x and --click-y must be provided together")] - IncompleteClickPoint, - - #[error("--drag-from-x, --drag-from-y, --drag-to-x, and --drag-to-y must be provided together")] - IncompleteDragPoints, - - #[error("--touch-x and --touch-y must be provided together")] - IncompleteTouchPoint, - - #[error("{name} path is empty")] - EmptyPath { name: &'static str }, - - #[error("invalid --site-permission JSON: {value}")] - InvalidSitePermissionJson { - value: String, - #[source] - source: serde_json::Error, - }, - - #[error( - "invalid --rendering-context value: {value:?} (expected \"software\" or \ - \"hardware\")" - )] - InvalidRenderingContext { value: String }, - - #[error(transparent)] - Domain(#[from] ely_domain::DomainError), -} - -pub(super) fn parse_env_command() -> Result { - parse_command(env::args()) -} - -fn parse_command( - args: impl IntoIterator, -) -> Result { - let mut args = args.into_iter(); - let _program_name = args.next(); - let command = args.next().ok_or(SidecarArgsError::MissingCommand)?; - - match command.as_str() { - "live" => parse_live_args(args).map(SidecarCommand::Live), - "snapshot" => parse_snapshot_args(args).map(SidecarCommand::Snapshot), - _ => Err(SidecarArgsError::UnknownCommand { value: command }), - } -} - -fn parse_live_args(args: impl IntoIterator) -> Result { - let mut args = args.into_iter(); - let mut profile_data_dir = None; - let mut iosurface_mach_service = None; - let mut rendering_context_kind = RenderingContextKind::default(); - - while let Some(name) = args.next() { - match name.as_str() { - "--profile-data-dir" => { - profile_data_dir = Some(parse_path( - "--profile-data-dir", - next_argument(&mut args, "--profile-data-dir")?, - )?) - } - "--rendering-context" => { - let value = next_argument(&mut args, "--rendering-context")?; - rendering_context_kind = match value.as_str() { - "software" => RenderingContextKind::Software, - "hardware" => RenderingContextKind::Hardware, - _ => return Err(SidecarArgsError::InvalidRenderingContext { value }), - }; - } - "--iosurface-mach-service" => { - iosurface_mach_service = - Some(next_argument(&mut args, "--iosurface-mach-service")?); - } - _ => return Err(SidecarArgsError::UnknownArgument { value: name }), - } - } - - Ok(LiveArgs { - profile_data_dir: profile_data_dir - .ok_or(SidecarArgsError::MissingRequiredArgument { name: "--profile-data-dir" })?, - iosurface_mach_service, - rendering_context_kind, - }) -} - -fn parse_snapshot_args( - args: impl IntoIterator, -) -> Result { - let mut args = args.into_iter(); - let mut url = None; - let mut profile_id = None; - let mut profile_data_dir = None; - let mut rgba_out = None; - let mut width = None; - let mut height = None; - let mut scroll_x = 0; - let mut scroll_y = 0; - let mut page_zoom_percent = DEFAULT_ZOOM_PERCENT; - let mut click_x = None; - let mut click_y = None; - let mut drag_from_x = None; - let mut drag_from_y = None; - let mut drag_to_x = None; - let mut drag_to_y = None; - let mut touch_x = None; - let mut touch_y = None; - let mut typed_text = None; - let mut site_permissions = Vec::new(); - - while let Some(name) = args.next() { - match name.as_str() { - "--url" => url = Some(UrlText::parse(next_argument(&mut args, "--url")?)?), - "--profile-id" => { - profile_id = Some(ProfileId::parse(next_argument(&mut args, "--profile-id")?)?) - } - "--profile-data-dir" => { - profile_data_dir = Some(parse_path( - "--profile-data-dir", - next_argument(&mut args, "--profile-data-dir")?, - )?) - } - "--rgba-out" => { - rgba_out = Some(parse_path("--rgba-out", next_argument(&mut args, "--rgba-out")?)?) - } - "--width" => { - width = Some(parse_dimension("--width", next_argument(&mut args, "--width")?)?) - } - "--height" => { - height = Some(parse_dimension("--height", next_argument(&mut args, "--height")?)?) - } - "--scroll-x" => { - scroll_x = - parse_scroll_delta("--scroll-x", next_argument(&mut args, "--scroll-x")?)? - } - "--scroll-y" => { - scroll_y = - parse_scroll_delta("--scroll-y", next_argument(&mut args, "--scroll-y")?)? - } - "--page-zoom-percent" => { - page_zoom_percent = parse_zoom_percent( - "--page-zoom-percent", - next_argument(&mut args, "--page-zoom-percent")?, - )? - } - "--click-x" => { - click_x = Some(parse_click_coordinate( - "--click-x", - next_argument(&mut args, "--click-x")?, - )?) - } - "--click-y" => { - click_y = Some(parse_click_coordinate( - "--click-y", - next_argument(&mut args, "--click-y")?, - )?) - } - "--drag-from-x" => { - drag_from_x = Some(parse_click_coordinate( - "--drag-from-x", - next_argument(&mut args, "--drag-from-x")?, - )?) - } - "--drag-from-y" => { - drag_from_y = Some(parse_click_coordinate( - "--drag-from-y", - next_argument(&mut args, "--drag-from-y")?, - )?) - } - "--drag-to-x" => { - drag_to_x = Some(parse_click_coordinate( - "--drag-to-x", - next_argument(&mut args, "--drag-to-x")?, - )?) - } - "--drag-to-y" => { - drag_to_y = Some(parse_click_coordinate( - "--drag-to-y", - next_argument(&mut args, "--drag-to-y")?, - )?) - } - "--touch-x" => { - touch_x = Some(parse_click_coordinate( - "--touch-x", - next_argument(&mut args, "--touch-x")?, - )?) - } - "--touch-y" => { - touch_y = Some(parse_click_coordinate( - "--touch-y", - next_argument(&mut args, "--touch-y")?, - )?) - } - "--type-text" => typed_text = Some(next_argument(&mut args, "--type-text")?), - "--site-permission" => site_permissions - .push(parse_site_permission(next_argument(&mut args, "--site-permission")?)?), - _ => return Err(SidecarArgsError::UnknownArgument { value: name }), - } - } - - let click_point = match (click_x, click_y) { - (Some(x), Some(y)) => Some(ClickPoint { x, y }), - (None, None) => None, - _ => return Err(SidecarArgsError::IncompleteClickPoint), - }; - let drag_points = match (drag_from_x, drag_from_y, drag_to_x, drag_to_y) { - (Some(from_x), Some(from_y), Some(to_x), Some(to_y)) => Some(DragPoints { - from: ClickPoint { x: from_x, y: from_y }, - to: ClickPoint { x: to_x, y: to_y }, - }), - (None, None, None, None) => None, - _ => return Err(SidecarArgsError::IncompleteDragPoints), - }; - let touch_point = match (touch_x, touch_y) { - (Some(x), Some(y)) => Some(ClickPoint { x, y }), - (None, None) => None, - _ => return Err(SidecarArgsError::IncompleteTouchPoint), - }; - - Ok(SnapshotArgs { - url: url.ok_or(SidecarArgsError::MissingRequiredArgument { name: "--url" })?, - profile_id: profile_id - .ok_or(SidecarArgsError::MissingRequiredArgument { name: "--profile-id" })?, - profile_data_dir: profile_data_dir - .ok_or(SidecarArgsError::MissingRequiredArgument { name: "--profile-data-dir" })?, - rgba_out: rgba_out - .ok_or(SidecarArgsError::MissingRequiredArgument { name: "--rgba-out" })?, - width: width.ok_or(SidecarArgsError::MissingRequiredArgument { name: "--width" })?, - height: height.ok_or(SidecarArgsError::MissingRequiredArgument { name: "--height" })?, - scroll_x, - scroll_y, - page_zoom_percent, - click_point, - drag_points, - touch_point, - typed_text, - site_permissions, - }) -} - -#[derive(Deserialize)] -struct SitePermissionArg { - origin: String, - feature: String, - decision: String, -} - -fn parse_site_permission(value: String) -> Result { - let parsed: SitePermissionArg = serde_json::from_str(&value) - .map_err(|source| SidecarArgsError::InvalidSitePermissionJson { value, source })?; - - Ok(SidecarSitePermission { - origin: SiteOrigin::parse(parsed.origin)?, - feature: SitePermissionFeature::parse(parsed.feature.as_str())?, - decision: SitePermissionDecision::parse(parsed.decision.as_str())?, - }) -} - -fn next_argument( - args: &mut impl Iterator, - name: &'static str, -) -> Result { - args.next().ok_or(SidecarArgsError::MissingArgumentValue { name }) -} - -fn parse_dimension(name: &'static str, value: String) -> Result { - let dimension = value.parse::().map_err(|source| SidecarArgsError::InvalidInteger { - name, - value, - source, - })?; - if dimension == 0 { - return Err(SidecarArgsError::ZeroDimension { name }); - } - - Ok(dimension) -} - -fn parse_scroll_delta(name: &'static str, value: String) -> Result { - value.parse::().map_err(|source| SidecarArgsError::InvalidInteger { name, value, source }) -} - -fn parse_click_coordinate(name: &'static str, value: String) -> Result { - value.parse::().map_err(|source| SidecarArgsError::InvalidInteger { name, value, source }) -} - -fn parse_zoom_percent(name: &'static str, value: String) -> Result { - let percent = value.parse::().map_err(|source| SidecarArgsError::InvalidInteger { - name, - value, - source, - })?; - Ok(validate_zoom_percent(percent)?) -} - -fn parse_path(name: &'static str, value: String) -> Result { - if value.trim().is_empty() { - return Err(SidecarArgsError::EmptyPath { name }); - } - - Ok(PathBuf::from(value)) -} - -#[cfg(test)] -#[path = "args_tests.rs"] -mod tests; diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/args_tests.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/args_tests.rs deleted file mode 100644 index aa733a4..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/args_tests.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::{env, path::PathBuf}; - -use super::{SidecarArgsError, SidecarCommand, parse_command}; -use ely_domain::{ - DEFAULT_ZOOM_PERCENT, DomainError, ProfileId, SitePermissionDecision, SitePermissionFeature, -}; -use ely_servo_host::RenderingContextKind; - -#[test] -fn parses_snapshot_profile_identity() -> Result<(), SidecarArgsError> { - let profile_id = ProfileId::new(); - let profile_data_dir = env::temp_dir().join(profile_id.as_str()); - let args = parse_snapshot_command(&profile_id, profile_data_dir.clone())?; - - assert_eq!(args.profile_id, profile_id); - assert_eq!(args.profile_data_dir, profile_data_dir); - assert_eq!(args.page_zoom_percent, DEFAULT_ZOOM_PERCENT); - Ok(()) -} - -#[test] -fn rejects_invalid_snapshot_profile_id() { - let command = parse_command( - [ - "ely_servo_sidecar", - "snapshot", - "--url", - "https://example.com", - "--profile-id", - "profile_invalid", - "--profile-data-dir", - "/tmp/profile", - "--rgba-out", - "/tmp/frame.rgba", - "--width", - "64", - "--height", - "64", - ] - .into_iter() - .map(str::to_string), - ); - - assert!(matches!(command, Err(SidecarArgsError::Domain(DomainError::InvalidEntityId { .. })))); -} - -#[test] -fn parses_snapshot_site_permissions() -> Result<(), SidecarArgsError> { - let profile_id = ProfileId::new(); - let profile_data_dir = env::temp_dir().join(profile_id.as_str()); - let mut command = snapshot_command_args(&profile_id, profile_data_dir); - command.push("--site-permission".to_string()); - command.push( - r#"{"origin":"https://example.com","feature":"camera","decision":"allow-once"}"# - .to_string(), - ); - - let args = snapshot_args(parse_command(command)?); - assert_eq!(args.site_permissions.len(), 1); - let permission = &args.site_permissions[0]; - assert_eq!(permission.origin.as_str(), "https://example.com"); - assert_eq!(permission.feature, SitePermissionFeature::Camera); - assert_eq!(permission.decision, SitePermissionDecision::AllowOnce); - Ok(()) -} - -#[test] -fn parses_snapshot_page_zoom_percent() -> Result<(), SidecarArgsError> { - let profile_id = ProfileId::new(); - let profile_data_dir = env::temp_dir().join(profile_id.as_str()); - let mut command = snapshot_command_args(&profile_id, profile_data_dir); - command.push("--page-zoom-percent".to_string()); - command.push("125".to_string()); - - let args = snapshot_args(parse_command(command)?); - assert_eq!(args.page_zoom_percent, 125); - Ok(()) -} - -#[test] -fn rejects_out_of_range_snapshot_page_zoom_percent() { - let profile_id = ProfileId::new(); - let profile_data_dir = env::temp_dir().join(profile_id.as_str()); - let mut command = snapshot_command_args(&profile_id, profile_data_dir); - command.push("--page-zoom-percent".to_string()); - command.push("5".to_string()); - - assert!(matches!( - parse_command(command), - Err(SidecarArgsError::Domain(DomainError::InvalidZoomPercent { value: 5, .. })) - )); -} - -fn parse_snapshot_command( - profile_id: &ProfileId, - profile_data_dir: PathBuf, -) -> Result { - Ok(snapshot_args(parse_command(snapshot_command_args(profile_id, profile_data_dir))?)) -} - -fn snapshot_args(command: SidecarCommand) -> super::SnapshotArgs { - match command { - SidecarCommand::Snapshot(args) => args, - SidecarCommand::Live(_) => unreachable!("expected snapshot command"), - } -} - -fn snapshot_command_args(profile_id: &ProfileId, profile_data_dir: PathBuf) -> Vec { - [ - "ely_servo_sidecar".to_string(), - "snapshot".to_string(), - "--url".to_string(), - "https://example.com".to_string(), - "--profile-id".to_string(), - profile_id.as_str().to_string(), - "--profile-data-dir".to_string(), - profile_data_dir.display().to_string(), - "--rgba-out".to_string(), - "/tmp/frame.rgba".to_string(), - "--width".to_string(), - "64".to_string(), - "--height".to_string(), - "64".to_string(), - ] - .into_iter() - .collect() -} - -fn parse_live(extra_args: &[&str]) -> Result { - let base = ["ely_servo_sidecar", "live", "--profile-data-dir", "/tmp/sidecar-live"]; - let argv: Vec = - base.iter().chain(extra_args.iter()).map(|s| (*s).to_string()).collect(); - let SidecarCommand::Live(args) = parse_command(argv)? else { - return Err(SidecarArgsError::UnknownCommand { - value: "live-extracted-as-snapshot".into(), - }); - }; - Ok(args) -} - -#[test] -fn live_defaults_rendering_context_to_software() -> Result<(), SidecarArgsError> { - let args = parse_live(&[])?; - assert_eq!(args.rendering_context_kind, RenderingContextKind::Software); - Ok(()) -} - -#[test] -fn live_accepts_explicit_software_rendering_context() -> Result<(), SidecarArgsError> { - let args = parse_live(&["--rendering-context", "software"])?; - assert_eq!(args.rendering_context_kind, RenderingContextKind::Software); - Ok(()) -} - -#[test] -fn live_accepts_explicit_hardware_rendering_context() -> Result<(), SidecarArgsError> { - let args = parse_live(&["--rendering-context", "hardware"])?; - assert_eq!(args.rendering_context_kind, RenderingContextKind::Hardware); - Ok(()) -} - -#[test] -fn live_accepts_iosurface_mach_service_name() -> Result<(), SidecarArgsError> { - let args = parse_live(&["--iosurface-mach-service", "com.ely.test.iosurface"])?; - assert_eq!(args.iosurface_mach_service.as_deref(), Some("com.ely.test.iosurface")); - Ok(()) -} - -#[test] -fn live_rejects_unknown_rendering_context_value() { - assert!(matches!( - parse_live(&["--rendering-context", "gpu"]), - Err(SidecarArgsError::InvalidRenderingContext { value }) if value == "gpu" - )); -} - -#[test] -fn live_requires_rendering_context_value() { - assert!(matches!( - parse_live(&["--rendering-context"]), - Err(SidecarArgsError::MissingArgumentValue { name: "--rendering-context" }) - )); -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/iosurface_mach.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/iosurface_mach.rs deleted file mode 100644 index 1dd0b45..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/iosurface_mach.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::{ffi::CString, mem, time::Duration}; - -use mach2::{ - bootstrap::{bootstrap_look_up, bootstrap_port}, - kern_return::KERN_SUCCESS, - mach_port::mach_port_deallocate, - message::{ - MACH_MSG_SUCCESS, MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND, MACH_MSGH_BITS, - MACH_MSGH_BITS_COMPLEX, MACH_SEND_MSG, MACH_SEND_TIMEOUT, mach_msg, mach_msg_body_t, - mach_msg_header_t, mach_msg_port_descriptor_t, - }, - port::{MACH_PORT_NULL, mach_port_t}, - traps::mach_task_self, -}; -use thiserror::Error; - -use super::live_protocol::{LiveOutcome, LiveSidecarError}; - -const IOSURFACE_PORT_MESSAGE_ID: i32 = 0x454c_5901; -const SEND_TIMEOUT: Duration = Duration::from_secs(1); - -pub(super) struct IOSurfaceMachSender { - send_port: mach_port_t, -} - -#[derive(Debug, Error)] -pub(super) enum IOSurfaceMachError { - #[error("Mach service name contains an interior nul byte")] - InvalidServiceName, - - #[error("bootstrap_look_up returned {code}")] - LookupService { code: i32 }, - - #[error("mach_msg send returned {code}")] - Send { code: i32 }, -} - -impl IOSurfaceMachSender { - pub(super) fn connect(service_name: &str) -> Result { - let service_name = - CString::new(service_name).map_err(|_| IOSurfaceMachError::InvalidServiceName)?; - let mut send_port = MACH_PORT_NULL; - #[expect(unsafe_code)] - let result = - unsafe { bootstrap_look_up(bootstrap_port, service_name.as_ptr(), &mut send_port) }; - if result != KERN_SUCCESS { - return Err(IOSurfaceMachError::LookupService { code: result }); - } - Ok(Self { send_port }) - } - - pub(super) fn send_surface_port( - &mut self, - surface_id: u64, - mach_port: mach_port_t, - ) -> Result<(), IOSurfaceMachError> { - let mut message = IOSurfacePortMessage { - header: mach_msg_header_t { - msgh_bits: MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX, - msgh_size: mem::size_of::() as u32, - msgh_remote_port: self.send_port, - msgh_local_port: MACH_PORT_NULL, - msgh_voucher_port: MACH_PORT_NULL, - msgh_id: IOSURFACE_PORT_MESSAGE_ID, - }, - body: mach_msg_body_t { msgh_descriptor_count: 1 }, - surface_port: mach_msg_port_descriptor_t::new(mach_port, MACH_MSG_TYPE_MOVE_SEND), - surface_id, - }; - #[expect(unsafe_code)] - let result = unsafe { - mach_msg( - &mut message.header, - MACH_SEND_MSG | MACH_SEND_TIMEOUT, - message.header.msgh_size, - 0, - MACH_PORT_NULL, - timeout_millis(SEND_TIMEOUT), - MACH_PORT_NULL, - ) - }; - if result != MACH_MSG_SUCCESS { - destroy_message(&mut message); - return Err(IOSurfaceMachError::Send { code: result }); - } - Ok(()) - } -} - -impl Drop for IOSurfaceMachSender { - fn drop(&mut self) { - #[expect(unsafe_code)] - let task = unsafe { mach_task_self() }; - #[expect(unsafe_code)] - unsafe { - let _ = mach_port_deallocate(task, self.send_port); - } - } -} - -pub(super) fn send_surface_port_if_needed( - sender: Option<&mut IOSurfaceMachSender>, - outcome: &mut Result, -) { - let (Some(sender), Ok(live_outcome)) = (sender, outcome.as_ref()) else { - return; - }; - let Some(handle) = live_outcome.response.surface_handle else { - return; - }; - if let Err(error) = sender.send_surface_port(handle.surface_id, handle.mach_port_name) { - *outcome = Err(LiveSidecarError::IOSurfaceMach(error)); - } -} - -#[repr(C)] -struct IOSurfacePortMessage { - header: mach_msg_header_t, - body: mach_msg_body_t, - surface_port: mach_msg_port_descriptor_t, - surface_id: u64, -} - -fn timeout_millis(timeout: Duration) -> u32 { - u32::try_from(timeout.as_millis()).unwrap_or(u32::MAX).max(1) -} - -fn destroy_message(message: &mut IOSurfacePortMessage) { - #[expect(unsafe_code)] - unsafe { - mach2::message::mach_msg_destroy(&mut message.header); - } -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/live.rs deleted file mode 100644 index e5b692a..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live.rs +++ /dev/null @@ -1,476 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - fs, - io::{self, BufRead}, - time::Instant, -}; - -use ely_domain::{ProfileId, TabId, UrlText}; -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -use ely_servo_host::ServoHostError; -use ely_servo_host::{ - IOSurfaceIdentity, NavigationRequest, RenderingContextKind, ServoHost, ServoSurfaceSize, - SoftwareServoHost, -}; - -use super::args::LiveArgs; -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -use super::iosurface_mach::{IOSurfaceMachSender, send_surface_port_if_needed}; -use super::live_output::{populate_surface_fields, write_outcome}; -pub(super) use super::live_protocol::LiveSidecarError; -use super::live_protocol::{LiveFrameReport, LiveOutcome, LiveRequest, PartialFrameTimings}; -use super::live_session::{ - LiveInput, LiveSession, apply_input, apply_layout, apply_permissions, ensure_session, -}; -use super::perf::{FramePerfAggregator, FramePerfSummary, elapsed_ns}; - -pub(super) fn run_live(args: LiveArgs) -> Result<(), LiveSidecarError> { - let LiveArgs { profile_data_dir, iosurface_mach_service, rendering_context_kind } = args; - let publish_readback_surface_fields = true; - let require_client_ready_surfaces = iosurface_mach_service.is_some(); - fs::create_dir_all(&profile_data_dir)?; - let context_label = rendering_context_label(rendering_context_kind); - let mut host = SoftwareServoHost::new_with_config_dir_and_kind( - ServoSurfaceSize::new(1, 1), - Some(profile_data_dir), - rendering_context_kind, - )?; - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - let mut iosurface_mach_sender = - iosurface_mach_service.as_deref().map(IOSurfaceMachSender::connect).transpose()?; - #[cfg(not(all(feature = "hardware-render", target_os = "macos")))] - let _ = iosurface_mach_service; - let mut sessions = HashMap::new(); - let mut perf = - FramePerfAggregator::new(context_label, FramePerfAggregator::DEFAULT_WINDOW_SIZE); - let mut pending_summary: Option = None; - let mut published_surface_ids: HashMap> = HashMap::new(); - let stdin = io::stdin(); - let mut stdout = io::stdout().lock(); - - for line in stdin.lock().lines() { - let line = line?; - if line.trim().is_empty() { - continue; - } - - // `frame_started_at` is the honest start of the end-to-end - // frame: a request just arrived and we're about to do - // everything required to put bytes back on the pipe. The - // matching stop is the `stdout.flush()` inside - // `write_outcome`. - let frame_started_at = Instant::now(); - let outcome = match serde_json::from_str::(&line) { - Ok(request) => handle_request( - &mut host, - &mut sessions, - &mut published_surface_ids, - rendering_context_kind, - publish_readback_surface_fields, - require_client_ready_surfaces, - request, - ), - Err(error) => Err(LiveSidecarError::Json(error)), - }; - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - let outcome = { - let mut outcome = outcome; - send_surface_port_if_needed(iosurface_mach_sender.as_mut(), &mut outcome); - outcome - }; - write_outcome(&mut stdout, &mut perf, &mut pending_summary, outcome, frame_started_at)?; - } - - Ok(()) -} - -const fn rendering_context_label(kind: RenderingContextKind) -> &'static str { - match kind { - RenderingContextKind::Software => "software", - RenderingContextKind::Hardware => "hardware", - } -} - -fn handle_request( - host: &mut SoftwareServoHost, - sessions: &mut HashMap, - published_surface_ids: &mut HashMap>, - rendering_context_kind: RenderingContextKind, - publish_readback_surface_fields: bool, - require_client_ready_surfaces: bool, - request: LiveRequest, -) -> Result { - match request { - LiveRequest::Ensure { - tab_id, - profile_id, - url, - width, - height, - page_zoom_percent, - device_pixel_ratio, - scroll_delta_x, - scroll_delta_y, - scroll_point_x, - scroll_point_y, - click_x, - click_y, - hover_x, - hover_y, - typed_text, - site_permissions, - ready_surface_ids, - } => { - let tab = TabId::parse(tab_id.clone())?; - let profile = ProfileId::parse(profile_id)?; - let url = UrlText::parse(url)?; - let session = - ensure_session(host, sessions, tab_id.clone(), &tab, &profile, width, height)?; - - if apply_layout(host, session, width, height, page_zoom_percent, device_pixel_ratio)? { - session.awaiting_visible_frame = true; - } - apply_permissions(host, session, &profile, site_permissions)?; - if session.requested_url != url.as_str() { - host.navigate(NavigationRequest { - webview_id: session.webview_id.clone(), - tab_id: tab, - url: url.clone(), - })?; - session.requested_url = url.as_str().to_string(); - session.scroll_x = 0; - session.scroll_y = 0; - session.awaiting_visible_frame = true; - // New URL: the previous tab's pixels are no longer - // valid evidence that "we have visible content"; let - // the gate skip blank loading frames again. - session.ever_visible_frame = false; - } - let input = LiveInput { - scroll_delta_x, - scroll_delta_y, - scroll_point_x, - scroll_point_y, - click_x, - click_y, - hover_x, - hover_y, - typed_text, - }; - if apply_input(host, session, input)? { - // The app tick calls this sidecar synchronously from - // GPUI's update path. Mark that a fresh frame is - // desired, then let poll_frame take one event-loop - // step; a later 16 ms app tick will poll again if - // Servo has not painted yet. - session.awaiting_visible_frame = true; - } - let webview_id = session.webview_id.clone(); - let mut outcome = poll_frame( - host, - session, - rendering_context_kind, - payloadless_readiness( - &tab_id, - published_surface_ids, - &ready_surface_ids, - require_client_ready_surfaces, - ), - )?; - populate_surface_fields( - host, - &webview_id, - &tab_id, - published_surface_ids, - publish_readback_surface_fields, - &mut outcome, - ); - Ok(outcome) - } - LiveRequest::Poll { tab_id, ready_surface_ids } => { - let Some(session) = sessions.get_mut(&tab_id) else { - return Ok(LiveOutcome::empty()); - }; - let webview_id = session.webview_id.clone(); - let mut outcome = poll_frame( - host, - session, - rendering_context_kind, - payloadless_readiness( - &tab_id, - published_surface_ids, - &ready_surface_ids, - require_client_ready_surfaces, - ), - )?; - populate_surface_fields( - host, - &webview_id, - &tab_id, - published_surface_ids, - publish_readback_surface_fields, - &mut outcome, - ); - Ok(outcome) - } - LiveRequest::Close { tab_id } => { - if let Some(session) = sessions.remove(&tab_id) { - host.close_webview(&session.webview_id); - } - published_surface_ids.remove(&tab_id); - Ok(LiveOutcome::empty()) - } - } -} - -fn poll_frame( - host: &mut SoftwareServoHost, - session: &mut LiveSession, - rendering_context_kind: RenderingContextKind, - readiness: PayloadlessReadiness<'_>, -) -> Result { - host.tick(); - let snapshot = host.snapshot(&session.webview_id)?; - let has_pending_frame = snapshot.has_pending_frame(); - if !should_paint_live_frame(has_pending_frame, session.awaiting_visible_frame) { - return Ok(LiveOutcome::empty()); - } - - let (outcome, has_visible_content) = - paint_pending_frame(host, session, rendering_context_kind, readiness, has_pending_frame)?; - if has_visible_content { - session.awaiting_visible_frame = false; - session.ever_visible_frame = true; - return Ok(outcome); - } - if !session.awaiting_visible_frame { - return Ok(outcome); - } - - Ok(LiveOutcome::empty()) -} - -fn should_paint_live_frame(has_pending_frame: bool, awaiting_visible_frame: bool) -> bool { - has_pending_frame || awaiting_visible_frame -} - -fn paint_pending_frame( - host: &mut SoftwareServoHost, - session: &mut LiveSession, - rendering_context_kind: RenderingContextKind, - readiness: PayloadlessReadiness<'_>, - has_pending_frame: bool, -) -> Result<(LiveOutcome, bool), LiveSidecarError> { - #[cfg(not(all(feature = "hardware-render", target_os = "macos")))] - let _ = readiness; - - match rendering_context_kind { - RenderingContextKind::Software => paint_readback_frame(host, session, !has_pending_frame), - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - RenderingContextKind::Hardware => { - paint_hardware_surface_frame(host, session, readiness, has_pending_frame) - } - #[cfg(not(all(feature = "hardware-render", target_os = "macos")))] - RenderingContextKind::Hardware => paint_readback_frame(host, session, !has_pending_frame), - } -} - -fn paint_readback_frame( - host: &mut SoftwareServoHost, - session: &LiveSession, - wait_for_completion: bool, -) -> Result<(LiveOutcome, bool), LiveSidecarError> { - let paint_started_at = Instant::now(); - host.paint_with_readback(&session.webview_id, wait_for_completion)?; - let snapshot = host.snapshot(&session.webview_id)?; - let frame = host.last_rendered_frame()?; - let paint_ns = elapsed_ns(paint_started_at); - let encode_started_at = Instant::now(); - let has_visible_content = session.ever_visible_frame - || (frame.non_white_pixel_count() > 0 && frame.content_pixel_count() > 0); - let report = LiveFrameReport::new(&snapshot, &frame, session.device_pixel_ratio()); - let encode_ns = elapsed_ns(encode_started_at); - let timings = PartialFrameTimings { paint_ns, encode_ns }; - Ok((LiveOutcome::from_frame(report, frame, timings), has_visible_content)) -} - -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -fn paint_hardware_surface_frame( - host: &mut SoftwareServoHost, - session: &LiveSession, - readiness: PayloadlessReadiness<'_>, - has_pending_frame: bool, -) -> Result<(LiveOutcome, bool), LiveSidecarError> { - if !session.ever_visible_frame { - return paint_initial_hardware_surface_frame(host, session, !has_pending_frame); - } - if !payloadless_surface_pool_ready(readiness, session.width, session.height) { - return paint_readback_frame(host, session, !has_pending_frame); - } - let (outcome, _) = paint_hardware_surface_report(host, session, !has_pending_frame)?; - Ok((outcome, true)) -} - -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -fn paint_initial_hardware_surface_frame( - host: &mut SoftwareServoHost, - session: &LiveSession, - wait_for_completion: bool, -) -> Result<(LiveOutcome, bool), LiveSidecarError> { - let paint_started_at = Instant::now(); - host.paint_with_readback(&session.webview_id, wait_for_completion)?; - let snapshot = host.snapshot(&session.webview_id)?; - let frame = host.last_rendered_frame()?; - let paint_ns = elapsed_ns(paint_started_at); - let encode_started_at = Instant::now(); - let report = LiveFrameReport::new(&snapshot, &frame, session.device_pixel_ratio()); - let has_visible_content = frame.non_white_pixel_count() > 0 && frame.content_pixel_count() > 0; - let encode_ns = elapsed_ns(encode_started_at); - let timings = PartialFrameTimings { paint_ns, encode_ns }; - Ok((LiveOutcome::from_frame(report, frame, timings), has_visible_content)) -} - -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -fn paint_hardware_surface_report( - host: &mut SoftwareServoHost, - session: &LiveSession, - wait_for_completion: bool, -) -> Result<(LiveOutcome, IOSurfaceIdentity), LiveSidecarError> { - let paint_started_at = Instant::now(); - host.paint_without_readback_with_completion(&session.webview_id, wait_for_completion)?; - let snapshot = host.snapshot(&session.webview_id)?; - let identity = host.peek_iosurface_identity(&session.webview_id)?.ok_or_else(|| { - ServoHostError::HardwareSurfaceUnavailable { id: session.webview_id.clone() } - })?; - let paint_ns = elapsed_ns(paint_started_at); - let encode_started_at = Instant::now(); - let report = LiveFrameReport::from_surface( - &snapshot, - identity.width, - identity.height, - session.device_pixel_ratio(), - ); - let encode_ns = elapsed_ns(encode_started_at); - let timings = PartialFrameTimings { paint_ns, encode_ns }; - Ok((LiveOutcome::from_report(report, timings), identity)) -} - -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -fn payloadless_surface_pool_ready( - readiness: PayloadlessReadiness<'_>, - width: u32, - height: u32, -) -> bool { - let Some(published) = readiness.published_surface_ids.get(readiness.tab_id) else { - return false; - }; - let matching = published - .iter() - .filter(|identity| identity.width == width && identity.height == height) - .copied() - .collect::>(); - if matching.is_empty() { - return false; - } - !readiness.require_client_ready_surfaces - || matching - .iter() - .any(|identity| readiness.ready_surface_ids.contains(&identity.surface_id)) -} - -#[derive(Clone, Copy)] -struct PayloadlessReadiness<'a> { - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - tab_id: &'a str, - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - published_surface_ids: &'a HashMap>, - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - ready_surface_ids: &'a [u64], - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - require_client_ready_surfaces: bool, - #[cfg(not(all(feature = "hardware-render", target_os = "macos")))] - _marker: std::marker::PhantomData<&'a ()>, -} - -fn payloadless_readiness<'a>( - tab_id: &'a str, - published_surface_ids: &'a HashMap>, - ready_surface_ids: &'a [u64], - require_client_ready_surfaces: bool, -) -> PayloadlessReadiness<'a> { - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - { - PayloadlessReadiness { - tab_id, - published_surface_ids, - ready_surface_ids, - require_client_ready_surfaces, - } - } - #[cfg(not(all(feature = "hardware-render", target_os = "macos")))] - { - let _ = (tab_id, published_surface_ids, ready_surface_ids, require_client_ready_surfaces); - PayloadlessReadiness { _marker: std::marker::PhantomData } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn awaiting_visible_frame_forces_paint_without_pending_flag() { - assert!(should_paint_live_frame(false, true)); - } - - #[test] - fn idle_poll_waits_for_pending_frame() { - assert!(!should_paint_live_frame(false, false)); - assert!(should_paint_live_frame(true, false)); - } - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - #[test] - fn payloadless_pool_accepts_one_client_ready_surface() { - let published = published_identities([identity(7, 800, 600), identity(8, 800, 600)]); - - assert!(!payloadless_surface_pool_ready(readiness(&published, &[], true), 800, 600)); - assert!(payloadless_surface_pool_ready(readiness(&published, &[7], true), 800, 600)); - } - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - #[test] - fn payloadless_pool_uses_published_surfaces_for_no_mach_clients() { - let published = published_identities([identity(7, 800, 600), identity(8, 800, 600)]); - - assert!(payloadless_surface_pool_ready(readiness(&published, &[], false), 800, 600)); - } - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - fn readiness<'a>( - published_surface_ids: &'a HashMap>, - ready_surface_ids: &'a [u64], - require_client_ready_surfaces: bool, - ) -> PayloadlessReadiness<'a> { - PayloadlessReadiness { - tab_id: "tab", - published_surface_ids, - ready_surface_ids, - require_client_ready_surfaces, - } - } - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - fn published_identities( - identities: [IOSurfaceIdentity; 2], - ) -> HashMap> { - let mut published = HashMap::new(); - published.insert("tab".to_string(), identities.into_iter().collect()); - published - } - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - fn identity(surface_id: u64, width: u32, height: u32) -> IOSurfaceIdentity { - IOSurfaceIdentity { surface_id, width, height } - } -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_output.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_output.rs deleted file mode 100644 index 38736e9..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_output.rs +++ /dev/null @@ -1,348 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - io::Write, - time::{Duration, Instant}, -}; - -#[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] -use ely_servo_host::IOSurfaceHandle; -use ely_servo_host::{IOSurfaceIdentity, SoftwareServoHost}; - -use super::live_protocol::{LiveOutcome, LiveSidecarError, PartialFrameTimings}; -use super::perf::{FramePerfAggregator, FramePerfSummary, FrameStageTimings, elapsed_ns}; - -/// Populate the hardware surface protocol fields on `outcome`. Readback -/// warm-up frames publish IOSurface handles so the app can import them -/// on its dedicated importer thread before steady-state payloadless -/// frames select the rotating surface ids. Two pieces of state ride out -/// together: -/// -/// * `current_surface_id` — set on every payload-bearing hardware -/// frame so the receiver knows which previously-imported -/// `MTLTexture` to sample THIS frame. surfman's attached swap -/// chain rotates front/back surfaces, so this alternates between -/// a small set of ids. -/// * `surface_handle` — populated only the first time the sidecar -/// sees a given `surface_id`; the receiver imports the IOSurface -/// once and caches the resulting Metal texture. Minting a fresh -/// mach port per frame would leak ports — `IOSurfaceCreateMachPort` -/// hands out a new send right each call and they don't free -/// automatically until the receiver `mach_port_deallocate`s. -pub(super) fn populate_surface_fields( - host: &SoftwareServoHost, - webview_id: &ely_domain::WebViewId, - tab_id: &str, - published_surface_ids: &mut HashMap>, - publish_readback_surface_fields: bool, - outcome: &mut LiveOutcome, -) { - if outcome.response.frame.is_none() { - return; - } - if outcome.frame.is_some() && !publish_readback_surface_fields { - return; - } - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - { - let Ok(Some(identity)) = host.peek_iosurface_identity(webview_id) else { - return; - }; - if let Err(message) = require_report_matches_surface_identity(outcome, identity) { - *outcome = LiveOutcome::error(message); - return; - } - let handle = if surface_has_been_published(published_surface_ids, tab_id, identity) { - None - } else { - host.current_iosurface_handle(webview_id).ok().flatten() - }; - let publication = surface_publication_for(published_surface_ids, tab_id, identity, handle); - outcome.response.current_surface_id = publication.current_surface_id; - outcome.response.surface_handle = publication.surface_handle; - } - #[cfg(not(all(feature = "hardware-render", target_os = "macos")))] - { - let _ = (host, webview_id, tab_id, published_surface_ids, publish_readback_surface_fields); - } -} - -#[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] -fn surface_has_been_published( - published_surface_ids: &HashMap>, - tab_id: &str, - identity: IOSurfaceIdentity, -) -> bool { - published_surface_ids.get(tab_id).is_some_and(|published| published.contains(&identity)) -} - -#[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] -#[derive(Clone, Copy)] -struct SurfacePublication { - current_surface_id: Option, - surface_handle: Option, -} - -#[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] -fn surface_publication_for( - published_surface_ids: &mut HashMap>, - tab_id: &str, - identity: IOSurfaceIdentity, - handle: Option, -) -> SurfacePublication { - if surface_has_been_published(published_surface_ids, tab_id, identity) { - return SurfacePublication { - current_surface_id: Some(identity.surface_id), - surface_handle: None, - }; - } - - let Some(handle) = handle.filter(|handle| handle_matches_identity(*handle, identity)) else { - return SurfacePublication { current_surface_id: None, surface_handle: None }; - }; - - published_surface_ids - .entry(tab_id.to_string()) - .or_default() - .insert(IOSurfaceIdentity::from_handle(handle)); - SurfacePublication { current_surface_id: Some(handle.surface_id), surface_handle: Some(handle) } -} - -#[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] -fn handle_matches_identity(handle: IOSurfaceHandle, identity: IOSurfaceIdentity) -> bool { - handle.surface_id == identity.surface_id - && handle.width == identity.width - && handle.height == identity.height -} - -#[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] -fn require_report_matches_surface_identity( - outcome: &LiveOutcome, - identity: IOSurfaceIdentity, -) -> Result<(), String> { - let Some(frame) = outcome.response.frame.as_ref() else { - return Ok(()); - }; - if frame.width == identity.width && frame.height == identity.height { - return Ok(()); - } - - Err(format!( - "servo hardware surface size {}x{} did not match frame report {}x{}", - identity.width, identity.height, frame.width, frame.height, - )) -} - -/// Serialise the response then stream the optional raw RGBA frame on -/// the same stdout pipe. The client reads the JSON line, takes -/// `rgba_byte_count` from the report, then reads that many bytes -/// from the same stream — no temp file round-trip. -/// -/// After the bytes hit the pipe we fold paint/encode/write/total -/// timings into the aggregator. `total_ns` is the wall-clock span -/// from `frame_started_at` (request arrival) to the stdout flush -/// returning, so it captures every per-frame cost outside the three -/// measured stages. Any summary the aggregator emits is stashed on -/// `pending_summary` and rides out on the *next* response, because -/// the protocol is one-line-per-response and an unsolicited summary -/// line would desync the main process's read loop. -pub(super) fn write_outcome( - stdout: &mut impl Write, - perf: &mut FramePerfAggregator, - pending_summary: &mut Option, - outcome: Result, - frame_started_at: Instant, -) -> Result<(), LiveSidecarError> { - let mut outcome = outcome.unwrap_or_else(|error| LiveOutcome::error(error.to_string())); - let partial_timings = outcome.partial_timings.take(); - let frame_present = outcome.response.frame.is_some(); - if let Some(summary) = pending_summary.take() { - outcome.response.perf = Some(summary); - } - // Payloadless hardware frames carry only the IOSurface selector. - let drop_rgba_payload = - outcome.response.current_surface_id.is_some() && outcome.frame.is_none(); - if drop_rgba_payload && let Some(report) = outcome.response.frame.as_mut() { - report.rgba_byte_count = 0; - } - let write_started_at = Instant::now(); - serde_json::to_writer(&mut *stdout, &outcome.response)?; - stdout.write_all(b"\n")?; - if !drop_rgba_payload && let Some(frame) = outcome.frame.as_ref() { - stdout.write_all(frame.rgba_bytes())?; - } - stdout.flush()?; - if frame_present { - let write_ns = elapsed_ns(write_started_at); - let total_ns = elapsed_ns(frame_started_at); - let partial = partial_timings.unwrap_or(PartialFrameTimings { paint_ns: 0, encode_ns: 0 }); - let timings = FrameStageTimings::from_durations( - Duration::from_nanos(partial.paint_ns), - Duration::from_nanos(partial.encode_ns), - Duration::from_nanos(write_ns), - Duration::from_nanos(total_ns), - ); - if let Some(summary) = perf.record(timings) { - *pending_summary = Some(summary); - } - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::{collections::HashMap, error::Error, time::Instant}; - - use ely_servo_host::{IOSurfaceHandle, IOSurfaceIdentity}; - - use super::super::{ - live_protocol::{LiveFrameReport, LiveOutcome, PartialFrameTimings}, - perf::FramePerfAggregator, - }; - use super::{require_report_matches_surface_identity, surface_publication_for, write_outcome}; - - #[test] - fn unpublished_surface_without_handle_leaves_selector_empty() { - let mut published = HashMap::new(); - let publication = - surface_publication_for(&mut published, "tab-1", identity(7, 800, 600), None); - - assert_eq!(publication.current_surface_id, None); - assert!(publication.surface_handle.is_none()); - assert!(published.is_empty()); - } - - #[test] - fn unpublished_surface_with_matching_handle_publishes_selector_and_handle() { - let mut published = HashMap::new(); - let handle = handle(7, 800, 600); - let publication = - surface_publication_for(&mut published, "tab-1", identity(7, 800, 600), Some(handle)); - - assert_eq!(publication.current_surface_id, Some(7)); - assert_eq!(publication.surface_handle, Some(handle)); - assert!(published.get("tab-1").is_some_and(|ids| ids.contains(&identity(7, 800, 600)))); - } - - #[test] - fn published_surface_reuses_selector_without_republishing_handle() { - let mut published = HashMap::new(); - let handle = handle(7, 800, 600); - let _ = - surface_publication_for(&mut published, "tab-1", identity(7, 800, 600), Some(handle)); - let publication = - surface_publication_for(&mut published, "tab-1", identity(7, 800, 600), None); - - assert_eq!(publication.current_surface_id, Some(7)); - assert!(publication.surface_handle.is_none()); - } - - #[test] - fn same_surface_id_with_changed_dimensions_republishes_handle() { - let mut published = HashMap::new(); - let initial = handle(7, 800, 600); - let resized = handle(7, 1024, 768); - - let _ = - surface_publication_for(&mut published, "tab-1", identity(7, 800, 600), Some(initial)); - let publication = - surface_publication_for(&mut published, "tab-1", identity(7, 1024, 768), Some(resized)); - - assert_eq!(publication.current_surface_id, Some(7)); - assert_eq!(publication.surface_handle, Some(resized)); - } - - #[test] - fn hardware_report_mismatch_is_reported() -> Result<(), Box> { - let outcome = LiveOutcome::from_report( - report_with_size(2180, 1586), - PartialFrameTimings { paint_ns: 1_000, encode_ns: 2_000 }, - ); - - let error = match require_report_matches_surface_identity(&outcome, identity(7, 2168, 1566)) - { - Ok(()) => return Err("mismatched IOSurface dimensions must be reported".into()), - Err(error) => error, - }; - - assert_eq!( - error, - "servo hardware surface size 2168x1566 did not match frame report 2180x1586", - ); - Ok(()) - } - - #[test] - fn mismatched_handle_leaves_surface_unpublished() { - let mut published = HashMap::new(); - let publication = surface_publication_for( - &mut published, - "tab-1", - identity(7, 800, 600), - Some(handle(8, 800, 600)), - ); - - assert_eq!(publication.current_surface_id, None); - assert!(publication.surface_handle.is_none()); - assert!(published.is_empty()); - } - - #[test] - fn payloadless_surface_report_records_perf_and_writes_no_rgba() -> Result<(), Box> { - let mut outcome = LiveOutcome::from_report( - report_with_byte_count(16), - PartialFrameTimings { paint_ns: 1_000, encode_ns: 2_000 }, - ); - outcome.response.current_surface_id = Some(7); - let mut stdout = Vec::new(); - let mut perf = FramePerfAggregator::new("hardware", 1); - let mut pending_summary = None; - - write_outcome(&mut stdout, &mut perf, &mut pending_summary, Ok(outcome), Instant::now())?; - - let Some(newline_index) = stdout.iter().position(|byte| *byte == b'\n') else { - return Err("response newline missing".into()); - }; - let line = std::str::from_utf8(&stdout[..newline_index])?; - let response: serde_json::Value = serde_json::from_str(line)?; - let rgba_byte_count = response - .get("frame") - .and_then(|frame| frame.get("rgba_byte_count")) - .and_then(serde_json::Value::as_u64); - - assert_eq!(rgba_byte_count, Some(0)); - assert!(stdout[newline_index + 1..].is_empty()); - assert!(pending_summary.is_some()); - Ok(()) - } - - fn identity(surface_id: u64, width: u32, height: u32) -> IOSurfaceIdentity { - IOSurfaceIdentity { surface_id, width, height } - } - - fn handle(surface_id: u64, width: u32, height: u32) -> IOSurfaceHandle { - IOSurfaceHandle { mach_port_name: 42, surface_id, width, height } - } - - fn report_with_byte_count(rgba_byte_count: usize) -> LiveFrameReport { - let mut report = report_with_size(2, 2); - report.rgba_byte_count = rgba_byte_count; - report - } - - fn report_with_size(width: u32, height: u32) -> LiveFrameReport { - LiveFrameReport { - loaded_url: Some("https://example.com/".to_string()), - title: Some("Example".to_string()), - state: "complete", - width, - height, - device_pixel_ratio: 1.0, - css_viewport_width: width, - css_viewport_height: height, - rgba_byte_count: 0, - non_white_pixel_count: 0, - content_pixel_count: 0, - sample_hash: 0, - } - } -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_protocol.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_protocol.rs deleted file mode 100644 index f367c49..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_protocol.rs +++ /dev/null @@ -1,300 +0,0 @@ -//! Wire types for the sidecar live loop. Split out of `live.rs` to -//! keep the hot loop and protocol surface in separate files. - -use std::io; - -use ely_servo_host::{ - IOSurfaceHandle, RenderedFrame, ServoHostError, WebViewSnapshot, WebViewState, -}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -#[cfg(all(feature = "hardware-render", target_os = "macos"))] -use super::iosurface_mach::IOSurfaceMachError; -use super::perf::FramePerfSummary; - -#[derive(Deserialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub(super) enum LiveRequest { - Ensure { - tab_id: String, - profile_id: String, - url: String, - width: u32, - height: u32, - page_zoom_percent: u16, - /// Display scale factor reported by the host's window - /// (1.0 standard, 2.0 Retina). The sidecar plumbs this into - /// Servo's `WebView::set_hidpi_scale_factor` so CSS layout - /// happens at logical-pixel dimensions instead of physical. - /// Defaults to 1.0 for backward compatibility if a client - /// (e.g. the live perf bench) omits the field. - #[serde(default = "default_device_pixel_ratio")] - device_pixel_ratio: f32, - scroll_delta_x: i32, - scroll_delta_y: i32, - scroll_point_x: Option, - scroll_point_y: Option, - click_x: Option, - click_y: Option, - #[serde(default)] - hover_x: Option, - #[serde(default)] - hover_y: Option, - typed_text: Option, - site_permissions: Vec, - #[serde(default)] - ready_surface_ids: Vec, - }, - Poll { - tab_id: String, - #[serde(default)] - ready_surface_ids: Vec, - }, - Close { - tab_id: String, - }, -} - -fn default_device_pixel_ratio() -> f32 { - 1.0 -} - -#[derive(Deserialize)] -pub(super) struct LiveSitePermission { - pub origin: String, - pub feature: String, - pub decision: String, -} - -/// Partial stage timings captured inside `poll_frame` before the -/// write phase. Combined with the write-stage duration measured by -/// `write_outcome` to form a full set of frame timings. -#[derive(Clone, Copy, Debug)] -pub(super) struct PartialFrameTimings { - pub paint_ns: u64, - pub encode_ns: u64, -} - -/// A response plus an optional software RGBA payload and partial stage -/// timings. Software frames carry `RenderedFrame` so the write step -/// can stream its existing rgba slice straight onto the pipe; hardware -/// surface frames carry only a `LiveFrameReport`. -pub(super) struct LiveOutcome { - pub response: LiveResponse, - pub frame: Option, - pub partial_timings: Option, -} - -impl LiveOutcome { - pub fn empty() -> Self { - Self { response: LiveResponse::empty(), frame: None, partial_timings: None } - } - - pub fn error(message: String) -> Self { - Self { response: LiveResponse::error(message), frame: None, partial_timings: None } - } - - pub fn from_frame( - report: LiveFrameReport, - frame: RenderedFrame, - partial_timings: PartialFrameTimings, - ) -> Self { - Self { - response: LiveResponse::frame(report), - frame: Some(frame), - partial_timings: Some(partial_timings), - } - } - - #[cfg(any(test, all(feature = "hardware-render", target_os = "macos")))] - pub fn from_report(report: LiveFrameReport, partial_timings: PartialFrameTimings) -> Self { - Self { - response: LiveResponse::frame(report), - frame: None, - partial_timings: Some(partial_timings), - } - } -} - -#[derive(Serialize)] -pub(super) struct LiveResponse { - pub error: Option, - pub frame: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub perf: Option, - /// Populated on the first frame the sidecar emits for a given - /// surface — initial paint, after a resize, or whenever surfman - /// rotates its swap chain to a surface we haven't seen yet. The - /// receiver imports the IOSurface (via - /// `IOSurfaceLookupFromMachPort`) once per `surface_id` and caches - /// the resulting Metal texture. Always `None` on the software - /// path. - #[serde(skip_serializing_if = "Option::is_none")] - pub surface_handle: Option, - /// Populated on every hardware paint frame. Tells the receiver - /// which previously-imported IOSurface to sample THIS frame. The - /// surfman attached swap chain rotates between front/back - /// surfaces, so this id alternates between the values the receiver - /// has already imported. Always `None` on the software path. - #[serde(skip_serializing_if = "Option::is_none")] - pub current_surface_id: Option, -} - -impl LiveResponse { - fn empty() -> Self { - Self { - error: None, - frame: None, - perf: None, - surface_handle: None, - current_surface_id: None, - } - } - - fn frame(frame: LiveFrameReport) -> Self { - Self { - error: None, - frame: Some(frame), - perf: None, - surface_handle: None, - current_surface_id: None, - } - } - - fn error(message: String) -> Self { - Self { - error: Some(message), - frame: None, - perf: None, - surface_handle: None, - current_surface_id: None, - } - } -} - -#[derive(Serialize)] -pub(super) struct LiveFrameReport { - pub loaded_url: Option, - pub title: Option, - pub state: &'static str, - pub width: u32, - pub height: u32, - pub device_pixel_ratio: f32, - pub css_viewport_width: u32, - pub css_viewport_height: u32, - pub rgba_byte_count: usize, - pub non_white_pixel_count: u64, - pub content_pixel_count: u64, - pub sample_hash: u64, -} - -impl LiveFrameReport { - pub fn new(snapshot: &WebViewSnapshot, frame: &RenderedFrame, device_pixel_ratio: f32) -> Self { - let (css_viewport_width, css_viewport_height) = - css_viewport_size(frame.width(), frame.height(), device_pixel_ratio); - Self { - loaded_url: snapshot.url().map(str::to_string), - title: snapshot.title().map(str::to_string), - state: state_label(snapshot.state()), - width: frame.width(), - height: frame.height(), - device_pixel_ratio, - css_viewport_width, - css_viewport_height, - rgba_byte_count: frame.rgba_bytes().len(), - non_white_pixel_count: frame.non_white_pixel_count(), - content_pixel_count: frame.content_pixel_count(), - sample_hash: frame.sample_hash(), - } - } - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - pub fn from_surface( - snapshot: &WebViewSnapshot, - width: u32, - height: u32, - device_pixel_ratio: f32, - ) -> Self { - let (css_viewport_width, css_viewport_height) = - css_viewport_size(width, height, device_pixel_ratio); - Self { - loaded_url: snapshot.url().map(str::to_string), - title: snapshot.title().map(str::to_string), - state: state_label(snapshot.state()), - width, - height, - device_pixel_ratio, - css_viewport_width, - css_viewport_height, - rgba_byte_count: 0, - non_white_pixel_count: 0, - content_pixel_count: 0, - sample_hash: 0, - } - } -} - -fn css_viewport_size(width: u32, height: u32, device_pixel_ratio: f32) -> (u32, u32) { - let dpr = if device_pixel_ratio.is_finite() && device_pixel_ratio > 0.0 { - device_pixel_ratio - } else { - 1.0 - }; - ( - ((width as f32) / dpr).round().max(1.0) as u32, - ((height as f32) / dpr).round().max(1.0) as u32, - ) -} - -fn state_label(state: &WebViewState) -> &'static str { - match state { - WebViewState::Created => "created", - WebViewState::Loading => "loading", - WebViewState::Complete => "complete", - WebViewState::Sleeping => "sleeping", - WebViewState::Crashed => "crashed", - } -} - -#[derive(Debug, Error)] -pub(super) enum LiveSidecarError { - #[error("live session is unavailable after creation")] - SessionUnavailable, - - #[error("scroll input requires both scroll_point_x and scroll_point_y")] - IncompleteScrollPoint, - - #[error(transparent)] - Domain(#[from] ely_domain::DomainError), - - #[error(transparent)] - Host(#[from] ServoHostError), - - #[error(transparent)] - Io(#[from] io::Error), - - #[error(transparent)] - Json(#[from] serde_json::Error), - - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - #[error(transparent)] - IOSurfaceMach(#[from] IOSurfaceMachError), -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn close_request_deserializes_from_wire() -> Result<(), serde_json::Error> { - let request = - serde_json::from_str::(r#"{"type":"close","tab_id":"tab-live-close"}"#)?; - - assert!(matches!( - request, - LiveRequest::Close { tab_id } if tab_id == "tab-live-close" - )); - Ok(()) - } -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_session.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_session.rs deleted file mode 100644 index d2dfc9d..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/live_session.rs +++ /dev/null @@ -1,240 +0,0 @@ -use std::collections::HashMap; - -use ely_domain::{DEFAULT_ZOOM_PERCENT, ProfileId, TabId}; -use ely_servo_host::{ - KeyboardTextRequest, MouseClickRequest, MouseHoverRequest, PageZoomRequest, PermissionDecision, - PermissionRequest, ResizeRequest, ScrollRequest, ServoHost, ServoSurfaceSize, - SoftwareServoHost, -}; - -use super::live_protocol::{LiveSidecarError, LiveSitePermission}; - -#[derive(Clone)] -pub(super) struct LiveSession { - pub(super) webview_id: ely_domain::WebViewId, - pub(super) requested_url: String, - pub(super) width: u32, - pub(super) height: u32, - page_zoom_percent: u16, - /// Last hidpi factor pushed to Servo, encoded as `(scale × 1000)`. - /// Stored as a u32 so equality is cheap and stable across the - /// f32 jitter that JSON parsing can introduce. Init to 0 so the - /// first apply_layout call always pushes a real value. - hidpi_scale_milli: u32, - pub(super) scroll_x: i32, - pub(super) scroll_y: i32, - pub(super) awaiting_visible_frame: bool, - /// Sticky for the lifetime of a single URL: flipped to `true` - /// the first time `poll_frame` sees a paint with real content - /// (non-white, non-empty) and reset to `false` on every navigate. - /// After it's `true`, the visible-content gate stops gating: - /// scroll/click/hover/type all return on the first - /// `has_pending_frame=true` (~3 ms) instead of waiting the full - /// `LIVE_FRAME_WAIT_TIMEOUT`. The gate stays armed for the - /// initial paint of each new URL so loading frames are still - /// skipped. - pub(super) ever_visible_frame: bool, -} - -impl LiveSession { - fn new(webview_id: ely_domain::WebViewId, _width: u32, _height: u32) -> Self { - Self { - webview_id, - requested_url: String::new(), - width: 0, - height: 0, - page_zoom_percent: DEFAULT_ZOOM_PERCENT, - hidpi_scale_milli: 0, - scroll_x: 0, - scroll_y: 0, - awaiting_visible_frame: false, - ever_visible_frame: false, - } - } - - pub(super) fn device_pixel_ratio(&self) -> f32 { - hidpi_scale_milli_to_f32(self.hidpi_scale_milli) - } -} - -pub(super) fn ensure_session<'a>( - host: &mut SoftwareServoHost, - sessions: &'a mut HashMap, - key: String, - tab_id: &TabId, - profile_id: &ProfileId, - width: u32, - height: u32, -) -> Result<&'a mut LiveSession, LiveSidecarError> { - if !sessions.contains_key(&key) { - let webview_id = host.create_webview_with_size( - tab_id.clone(), - profile_id.clone(), - ServoSurfaceSize::new(width, height), - )?; - sessions.insert(key.clone(), LiveSession::new(webview_id, width, height)); - } - - sessions.get_mut(&key).ok_or(LiveSidecarError::SessionUnavailable) -} - -pub(super) fn apply_layout( - host: &mut SoftwareServoHost, - session: &mut LiveSession, - width: u32, - height: u32, - page_zoom_percent: u16, - device_pixel_ratio: f32, -) -> Result { - let mut changed = false; - // Push the device pixel ratio BEFORE resize. Servo's WebView - // defaults hidpi to 1.0; without this the first layout treats - // physical-pixel viewport widths as CSS-pixel widths and the page - // lays out half the size you'd expect on a Retina display. - let hidpi_scale_milli = encode_hidpi_scale_milli(device_pixel_ratio); - if session.hidpi_scale_milli != hidpi_scale_milli { - host.set_hidpi_scale(ely_servo_host::HidpiScaleRequest { - webview_id: session.webview_id.clone(), - scale_factor: hidpi_scale_milli_to_f32(hidpi_scale_milli), - })?; - session.hidpi_scale_milli = hidpi_scale_milli; - changed = true; - } - - if session.width != width || session.height != height { - host.resize(ResizeRequest { webview_id: session.webview_id.clone(), width, height })?; - session.width = width; - session.height = height; - changed = true; - } - - if session.page_zoom_percent != page_zoom_percent { - host.set_page_zoom(PageZoomRequest { - webview_id: session.webview_id.clone(), - zoom_factor: f32::from(page_zoom_percent) / 100.0, - })?; - session.page_zoom_percent = page_zoom_percent; - changed = true; - } - - Ok(changed) -} - -pub(super) fn apply_permissions( - host: &mut SoftwareServoHost, - session: &LiveSession, - profile_id: &ProfileId, - permissions: Vec, -) -> Result<(), LiveSidecarError> { - for permission in permissions { - host.set_permission( - PermissionRequest { - webview_id: session.webview_id.clone(), - profile_id: profile_id.clone(), - origin: ely_domain::SiteOrigin::parse(permission.origin)?, - feature: ely_domain::SitePermissionFeature::parse(permission.feature.as_str())?, - }, - PermissionDecision::from(ely_domain::SitePermissionDecision::parse( - permission.decision.as_str(), - )?), - )?; - } - - Ok(()) -} - -pub(super) fn apply_input( - host: &mut SoftwareServoHost, - session: &mut LiveSession, - input: LiveInput, -) -> Result { - let mut changed = false; - if input.scroll_delta_x != 0 || input.scroll_delta_y != 0 { - let (point_x, point_y) = input.scroll_point()?; - host.scroll(ScrollRequest { - webview_id: session.webview_id.clone(), - delta_x: input.scroll_delta_x, - delta_y: input.scroll_delta_y, - point_x, - point_y, - })?; - session.scroll_x = positive_scroll_component(session.scroll_x, input.scroll_delta_x); - session.scroll_y = positive_scroll_component(session.scroll_y, input.scroll_delta_y); - changed = true; - } - - if let (Some(x), Some(y)) = (input.hover_x, input.hover_y) { - host.hover(MouseHoverRequest { webview_id: session.webview_id.clone(), x, y })?; - changed = true; - } - - if let (Some(x), Some(y)) = (input.click_x, input.click_y) { - host.click(MouseClickRequest { webview_id: session.webview_id.clone(), x, y })?; - changed = true; - } - - if let Some(text) = input.typed_text { - host.type_text(KeyboardTextRequest { webview_id: session.webview_id.clone(), text })?; - changed = true; - } - - Ok(changed) -} - -pub(super) struct LiveInput { - pub(super) scroll_delta_x: i32, - pub(super) scroll_delta_y: i32, - pub(super) scroll_point_x: Option, - pub(super) scroll_point_y: Option, - pub(super) click_x: Option, - pub(super) click_y: Option, - pub(super) hover_x: Option, - pub(super) hover_y: Option, - pub(super) typed_text: Option, -} - -impl LiveInput { - fn scroll_point(&self) -> Result<(u32, u32), LiveSidecarError> { - let point = match (self.scroll_point_x, self.scroll_point_y) { - (Some(x), Some(y)) => (x, y), - _ => return Err(LiveSidecarError::IncompleteScrollPoint), - }; - Ok(point) - } -} - -fn encode_hidpi_scale_milli(scale: f32) -> u32 { - if !scale.is_finite() || scale <= 0.0 { - return 1_000; - } - let scaled = (scale * 1_000.0).round(); - scaled.clamp(500.0, 5_000.0) as u32 -} - -fn hidpi_scale_milli_to_f32(milli: u32) -> f32 { - milli as f32 / 1_000.0 -} - -fn positive_scroll_component(current: i32, delta: i32) -> i32 { - let value = i64::from(current) + i64::from(delta); - value.clamp(0, i64::from(i32::MAX)) as i32 -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn new_live_session_forces_first_resize_after_hidpi() { - let session = LiveSession::new(ely_domain::WebViewId::new(), 1280, 720); - - assert_ne!( - session.width, 1280, - "first apply_layout must resize after hidpi has been pushed", - ); - assert_ne!( - session.height, 720, - "first apply_layout must resize after hidpi has been pushed", - ); - } -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/perf.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/perf.rs deleted file mode 100644 index a570e9c..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/perf.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! Per-frame paint→encode→write stage timings for the live sidecar -//! loop, plus exact fixed-window percentile summaries so the main -//! process can read out p50/p95/p99 latencies. -//! -//! Why this lives next to `live.rs`: the sidecar already owns the hot -//! loop. Sampling here costs one `Instant::now()` per stage boundary. -//! Each stage keeps one preallocated window of nanosecond samples and -//! sorts only at the 60-frame summary boundary, so steady-state record -//! cost stays a single push per stage while p95 remains exact enough -//! for 120 fps gates. - -use std::time::{Duration, Instant}; - -/// Per-frame stage timings captured by the live loop. -/// -/// `total_ns` is the real wall-clock span from request arrival to the -/// stdout flush returning, so it captures every byte of overhead -/// outside paint/encode/write (snapshot reads, JSON parse, scratch -/// allocations). It is measured at the loop boundary, not summed. -#[derive(Clone, Copy, Debug)] -pub(super) struct FrameStageTimings { - pub paint_ns: u64, - pub encode_ns: u64, - pub write_ns: u64, - pub total_ns: u64, -} - -impl FrameStageTimings { - pub(super) fn from_durations( - paint: Duration, - encode: Duration, - write: Duration, - total: Duration, - ) -> Self { - Self { - paint_ns: duration_to_ns(paint), - encode_ns: duration_to_ns(encode), - write_ns: duration_to_ns(write), - total_ns: duration_to_ns(total), - } - } -} - -fn duration_to_ns(duration: Duration) -> u64 { - u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX) -} - -/// Saturating elapsed-ns helper. `Instant::elapsed` is monotonic but -/// the cast can still overflow on the (impossible) hour-long frame. -pub(super) fn elapsed_ns(start: Instant) -> u64 { - duration_to_ns(start.elapsed()) -} - -#[derive(Debug)] -struct StageSamples { - values: Vec, -} - -impl StageSamples { - fn new(window_size: usize) -> Self { - Self { values: Vec::with_capacity(window_size) } - } - - fn record(&mut self, ns: u64) { - self.values.push(ns); - } - - fn len(&self) -> usize { - self.values.len() - } - - fn percentiles_us(&self) -> StagePercentiles { - let mut sorted = self.values.clone(); - sorted.sort_unstable(); - StagePercentiles { - p50: percentile_us(&sorted, 0.50), - p95: percentile_us(&sorted, 0.95), - p99: percentile_us(&sorted, 0.99), - } - } - - fn reset(&mut self) { - self.values.clear(); - } -} - -#[derive(Clone, Copy)] -struct StagePercentiles { - p50: u64, - p95: u64, - p99: u64, -} - -fn percentile_us(sorted_ns: &[u64], percentile: f64) -> u64 { - if sorted_ns.is_empty() { - return 0; - } - let target = ((sorted_ns.len() as f64) * percentile).ceil() as usize; - let index = target.max(1).min(sorted_ns.len()) - 1; - ns_to_us_ceil(sorted_ns[index]) -} - -fn ns_to_us_ceil(ns: u64) -> u64 { - ns.div_ceil(1_000) -} - -/// Aggregates a rolling window of [`FrameStageTimings`] across N -/// frames, exposing one [`FramePerfSummary`] per window flush. -pub(super) struct FramePerfAggregator { - window_size: usize, - paint: StageSamples, - encode: StageSamples, - write: StageSamples, - total: StageSamples, - context_label: &'static str, -} - -impl FramePerfAggregator { - pub(super) const DEFAULT_WINDOW_SIZE: u32 = 60; - - pub(super) fn new(context_label: &'static str, window_size: u32) -> Self { - let window_size = usize::try_from(window_size.max(1)).unwrap_or(usize::MAX); - Self { - window_size, - paint: StageSamples::new(window_size), - encode: StageSamples::new(window_size), - write: StageSamples::new(window_size), - total: StageSamples::new(window_size), - context_label, - } - } - - pub(super) fn record(&mut self, timings: FrameStageTimings) -> Option { - self.paint.record(timings.paint_ns); - self.encode.record(timings.encode_ns); - self.write.record(timings.write_ns); - self.total.record(timings.total_ns); - if self.paint.len() < self.window_size { - return None; - } - let paint = self.paint.percentiles_us(); - let encode = self.encode.percentiles_us(); - let write = self.write.percentiles_us(); - let total = self.total.percentiles_us(); - let summary = FramePerfSummary { - window: u32::try_from(self.paint.len()).unwrap_or(u32::MAX), - context: self.context_label, - paint_p50_us: paint.p50, - paint_p95_us: paint.p95, - paint_p99_us: paint.p99, - encode_p50_us: encode.p50, - encode_p95_us: encode.p95, - encode_p99_us: encode.p99, - write_p50_us: write.p50, - write_p95_us: write.p95, - write_p99_us: write.p99, - total_p50_us: total.p50, - total_p95_us: total.p95, - total_p99_us: total.p99, - }; - self.paint.reset(); - self.encode.reset(); - self.write.reset(); - self.total.reset(); - Some(summary) - } -} - -#[derive(Clone, Copy, Debug, serde::Serialize)] -pub(super) struct FramePerfSummary { - pub window: u32, - pub context: &'static str, - pub paint_p50_us: u64, - pub paint_p95_us: u64, - pub paint_p99_us: u64, - pub encode_p50_us: u64, - pub encode_p95_us: u64, - pub encode_p99_us: u64, - pub write_p50_us: u64, - pub write_p95_us: u64, - pub write_p99_us: u64, - pub total_p50_us: u64, - pub total_p95_us: u64, - pub total_p99_us: u64, -} - -#[cfg(test)] -mod tests { - use super::{FramePerfAggregator, FrameStageTimings, percentile_us}; - use std::time::Duration; - - #[test] - fn percentile_us_uses_nearest_rank_and_ceils_microseconds() { - let sorted_ns = [1, 1_000, 1_001]; - assert_eq!(percentile_us(&sorted_ns, 0.50), 1); - assert_eq!(percentile_us(&sorted_ns, 0.95), 2); - assert_eq!(percentile_us(&sorted_ns, 0.99), 2); - } - - #[test] - fn aggregator_emits_summary_after_window_size_records() -> Result<(), &'static str> { - let mut aggregator = - FramePerfAggregator::new("software", FramePerfAggregator::DEFAULT_WINDOW_SIZE); - for index in 0..(FramePerfAggregator::DEFAULT_WINDOW_SIZE - 1) { - let result = aggregator.record(constant_timing()); - assert!(result.is_none(), "should not flush at frame {index}"); - } - let summary = aggregator - .record(constant_timing()) - .ok_or("aggregator must flush at window boundary")?; - assert_eq!(summary.window, FramePerfAggregator::DEFAULT_WINDOW_SIZE); - assert_eq!(summary.context, "software"); - Ok(()) - } - - #[test] - fn aggregator_resets_after_flush_so_next_window_starts_fresh() { - let mut aggregator = FramePerfAggregator::new("hardware", 2); - let _ = aggregator.record(constant_timing()); - let summary = aggregator.record(constant_timing()); - assert!(summary.is_some(), "expected first flush"); - let after_flush = aggregator.record(constant_timing()); - assert!(after_flush.is_none(), "aggregator must zero counters after flush"); - } - - #[test] - fn aggregator_percentiles_track_increasing_paint_durations() -> Result<(), &'static str> { - let mut aggregator = FramePerfAggregator::new("software", 4); - let paint_durations_us = [10u64, 100, 1_000, 10_000]; - let mut summary = None; - for paint_us in paint_durations_us { - summary = aggregator.record(FrameStageTimings::from_durations( - Duration::from_micros(paint_us), - Duration::from_micros(1), - Duration::from_micros(1), - Duration::from_micros(paint_us + 2), - )); - } - let summary = summary.ok_or("4-frame window must flush")?; - assert_eq!(summary.paint_p50_us, 100); - assert_eq!(summary.paint_p95_us, 10_000); - assert_eq!(summary.paint_p99_us, 10_000); - assert_eq!(summary.total_p50_us, 102); - assert_eq!(summary.total_p95_us, 10_002); - assert_eq!(summary.total_p99_us, 10_002); - Ok(()) - } - - fn constant_timing() -> FrameStageTimings { - FrameStageTimings::from_durations( - Duration::from_micros(2_000), - Duration::from_micros(500), - Duration::from_micros(100), - Duration::from_micros(2_600), - ) - } -} diff --git a/crates/ely_servo_host/src/bin/ely_servo_sidecar/report.rs b/crates/ely_servo_host/src/bin/ely_servo_sidecar/report.rs deleted file mode 100644 index 9584d7b..0000000 --- a/crates/ely_servo_host/src/bin/ely_servo_sidecar/report.rs +++ /dev/null @@ -1,98 +0,0 @@ -use ely_servo_host::{RenderedFrame, WebViewSnapshot, WebViewState}; -use serde::Serialize; - -use super::args::SnapshotArgs; - -pub(super) struct SnapshotInputChanges { - pub(super) scroll: bool, - pub(super) click: bool, - pub(super) drag: bool, - pub(super) touch: bool, - pub(super) text: bool, -} - -#[derive(Serialize)] -pub(super) struct SnapshotReport { - requested_url: String, - profile_id: String, - loaded_url: Option, - title: Option, - rgba_path: String, - state: &'static str, - width: u32, - height: u32, - rgba_byte_count: usize, - opaque_pixel_count: u64, - non_white_pixel_count: u64, - content_pixel_count: u64, - sample_hash: u64, - scroll_x: i32, - scroll_y: i32, - page_zoom_percent: u16, - scroll_changed_frame: bool, - click_x: Option, - click_y: Option, - click_changed_frame: bool, - drag_from_x: Option, - drag_from_y: Option, - drag_to_x: Option, - drag_to_y: Option, - drag_changed_frame: bool, - touch_x: Option, - touch_y: Option, - touch_changed_frame: bool, - typed_text_byte_count: usize, - text_changed_frame: bool, -} - -impl SnapshotReport { - pub(super) fn new( - args: &SnapshotArgs, - snapshot: &WebViewSnapshot, - frame: &RenderedFrame, - changes: SnapshotInputChanges, - ) -> Self { - Self { - requested_url: args.url.as_str().to_string(), - profile_id: snapshot.profile_id().as_str().to_string(), - loaded_url: snapshot.url().map(str::to_string), - title: snapshot.title().map(str::to_string), - rgba_path: args.rgba_out.display().to_string(), - state: state_label(snapshot.state()), - width: frame.width(), - height: frame.height(), - rgba_byte_count: frame.rgba_bytes().len(), - opaque_pixel_count: frame.opaque_pixel_count(), - non_white_pixel_count: frame.non_white_pixel_count(), - content_pixel_count: frame.content_pixel_count(), - sample_hash: frame.sample_hash(), - scroll_x: args.scroll_x, - scroll_y: args.scroll_y, - page_zoom_percent: args.page_zoom_percent, - scroll_changed_frame: changes.scroll, - click_x: args.click_point.map(|point| point.x), - click_y: args.click_point.map(|point| point.y), - click_changed_frame: changes.click, - drag_from_x: args.drag_points.map(|points| points.from.x), - drag_from_y: args.drag_points.map(|points| points.from.y), - drag_to_x: args.drag_points.map(|points| points.to.x), - drag_to_y: args.drag_points.map(|points| points.to.y), - drag_changed_frame: changes.drag, - touch_x: args.touch_point.map(|point| point.x), - touch_y: args.touch_point.map(|point| point.y), - touch_changed_frame: changes.touch, - typed_text_byte_count: args.typed_text.as_ref().map_or(0, String::len), - text_changed_frame: changes.text, - } - } -} - -fn state_label(state: &WebViewState) -> &'static str { - match state { - WebViewState::Created => "created", - WebViewState::Loading => "loading", - WebViewState::Complete => "complete", - WebViewState::Sleeping => "sleeping", - WebViewState::Crashed => "crashed", - } -} diff --git a/crates/ely_servo_host/src/error.rs b/crates/ely_servo_host/src/error.rs index 701e38b..67b2dac 100644 --- a/crates/ely_servo_host/src/error.rs +++ b/crates/ely_servo_host/src/error.rs @@ -24,21 +24,6 @@ pub enum ServoHostError { #[error("servo rendering context could not be made current")] RenderingContextNotCurrent, - #[error( - "hardware rendering context requested but the `hardware-render` feature \ - was not compiled in; rebuild with --features servo-engine,hardware-render" - )] - HardwareRenderUnavailable, - #[error("servo rendered frame is unavailable")] RenderedFrameUnavailable, - - #[error("servo hardware surface is unavailable for {id}")] - HardwareSurfaceUnavailable { id: WebViewId }, - - #[error("servo screenshot capture timed out for {id}")] - ScreenshotTimedOut { id: WebViewId }, - - #[error("servo screenshot capture failed: {reason}")] - ScreenshotUnavailable { reason: String }, } diff --git a/crates/ely_servo_host/src/hardware_rendering_context.rs b/crates/ely_servo_host/src/hardware_rendering_context.rs deleted file mode 100644 index 80952a9..0000000 --- a/crates/ely_servo_host/src/hardware_rendering_context.rs +++ /dev/null @@ -1,398 +0,0 @@ -//! Headless hardware [`RenderingContext`] for Servo, vendored from -//! `servo-paint-api`'s private `SurfmanRenderingContext` and reshaped -//! so it can be constructed without a `RawWindowHandle`. -//! -//! Why this file exists: `servo-paint-api 0.1` exposes three -//! constructors — `SoftwareRenderingContext` (CPU-only), -//! `WindowRenderingContext` (requires `DisplayHandle + WindowHandle`), -//! and `OffscreenRenderingContext` (must be a child of a -//! `WindowRenderingContext`). The sidecar process has no window, so -//! none of the three works for us when we want **hardware** -//! rasterising. The underlying `SurfmanRenderingContext` glue *can* -//! drive a hardware adapter against a `SurfaceType::Generic` -//! offscreen surface — that's exactly what we need — but its -//! constructor is `fn new` (private). Until Servo accepts an upstream -//! PR exposing a headless hardware constructor, this file vendors the -//! minimal slice of glue we need. -//! -//! Scope kept deliberately narrow: -//! -//! * `prepare_for_rendering`, `read_to_image`, `size`, `resize`, -//! `present`, `make_current`, `gleam_gl_api`, `glow_gl_api`, and -//! `connection` are vendored. `connection` is mandatory: -//! `servo-paint`'s painter calls `rendering_context.connection() -//! .expect("Failed to get connection")` while constructing its -//! painter, so a `None` default panics the compositor before the -//! first frame is ever painted. -//! * `create_texture`/`destroy_texture` still fall through to the -//! trait defaults — Servo only reaches for them when sharing -//! surfman surfaces with its compositor for WebGL/WebGPU, which -//! this readback path does not exercise. -//! * No `RefreshDriver`. The sidecar drives its own polling loop. -//! * The reading path inlines `read_framebuffer_to_image` from the -//! same upstream file so we don't take a dependency on a private -//! helper that may change shape. -//! -//! This is feature-gated on `hardware-render`. The default build path -//! (and every existing test in this repo) keeps using -//! `SoftwareRenderingContext`; the hardware constructor only exists -//! when the feature is enabled, which is also when the additional -//! surfman/gleam/glow deps are pulled in. - -#![cfg(feature = "hardware-render")] - -use std::cell::{Cell, RefCell}; -use std::rc::Rc; -use std::sync::Arc; - -use dpi::PhysicalSize; -use euclid::Size2D; -use gleam::gl::{self, Gl}; -use image::RgbaImage; -use servo::{DeviceIntRect, RenderingContext}; -use surfman::chains::{PreserveBuffer, SwapChain, SwapChainAPI}; -#[cfg(target_os = "macos")] -use surfman::platform::macos::cgl::surface::NativeSurface; -use surfman::{ - Connection, Context, ContextAttributeFlags, ContextAttributes, Device, Error as SurfmanError, - GLApi, NativeWidget, Surface, SurfaceAccess, SurfaceType, -}; - -/// A headless hardware-backed [`RenderingContext`]. -/// -/// Construct with [`HardwareOffscreenContext::new`]; drop normally to -/// release the surfman context, surface, and swap chain. -pub struct HardwareOffscreenContext { - size: Cell>, - inner: SurfmanInner, - swap_chain: SwapChain, - #[cfg(target_os = "macos")] - held_presented_surface: RefCell>, - #[cfg(target_os = "macos")] - last_presented_iosurface: RefCell>, -} - -impl HardwareOffscreenContext { - /// Build a new hardware context with an offscreen - /// [`SurfaceType::Generic`] surface of the requested size. - /// - /// Uses `Connection::new()` to pick the platform default - /// (CGL on macOS — which backs surfaces with `IOSurface`s — - /// EGL on Linux, WGL on Windows) and `create_adapter()` for the - /// real GPU adapter. Falls back nowhere: if the host can't give - /// us a hardware GL context, the returned `Err` carries the - /// surfman cause and the caller is expected to either retry with - /// the software path or surface the failure. - pub fn new(size: PhysicalSize) -> Result { - let connection = Connection::new()?; - let adapter = connection.create_adapter()?; - let inner = SurfmanInner::new(&connection, &adapter)?; - let surfman_size = Size2D::new(size.width as i32, size.height as i32); - let surface = inner.create_surface(SurfaceType::Generic { size: surfman_size })?; - inner.bind_surface(surface)?; - inner.make_current()?; - let swap_chain = inner.create_attached_swap_chain()?; - Ok(Self { - size: Cell::new(size), - inner, - swap_chain, - #[cfg(target_os = "macos")] - held_presented_surface: RefCell::new(None), - #[cfg(target_os = "macos")] - last_presented_iosurface: RefCell::new(None), - }) - } -} - -impl Drop for HardwareOffscreenContext { - fn drop(&mut self) { - let device = &mut self.inner.device.borrow_mut(); - let context = &mut self.inner.context.borrow_mut(); - #[cfg(target_os = "macos")] - self.destroy_held_presented_surface(device, context); - let _ = self.swap_chain.destroy(device, context); - } -} - -impl RenderingContext for HardwareOffscreenContext { - fn prepare_for_rendering(&self) { - self.inner.prepare_for_rendering(); - } - - fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option { - self.inner.read_to_image(source_rectangle) - } - - fn size(&self) -> PhysicalSize { - self.size.get() - } - - fn resize(&self, size: PhysicalSize) { - if self.size.get() == size { - return; - } - - self.size.set(size); - - let device = &mut self.inner.device.borrow_mut(); - let context = &mut self.inner.context.borrow_mut(); - #[cfg(target_os = "macos")] - self.destroy_held_presented_surface(device, context); - let size = Size2D::new(size.width as i32, size.height as i32); - let _ = self.swap_chain.resize(device, context, size); - } - - fn present(&self) { - let device = &mut self.inner.device.borrow_mut(); - let context = &mut self.inner.context.borrow_mut(); - #[cfg(target_os = "macos")] - self.recycle_held_presented_surface(); - let _ = self.swap_chain.swap_buffers(device, context, PreserveBuffer::No); - #[cfg(target_os = "macos")] - self.capture_presented_iosurface(device); - } - - fn make_current(&self) -> Result<(), SurfmanError> { - self.inner.make_current() - } - - fn gleam_gl_api(&self) -> Rc { - self.inner.gleam_gl.clone() - } - - fn glow_gl_api(&self) -> Arc { - self.inner.glow_gl.clone() - } - - fn connection(&self) -> Option { - Some(self.inner.device.borrow().connection()) - } -} - -#[cfg(target_os = "macos")] -use crate::iosurface_handle::{IOSurfaceHandle, IOSurfaceIdentity}; - -#[cfg(target_os = "macos")] -impl HardwareOffscreenContext { - /// Cheap, non-mutating identity probe of the IOSurface that was - /// just presented. Used by the sidecar to dedup mach port creation. - pub fn peek_iosurface_identity(&self) -> Result, SurfmanError> { - Ok(self.last_presented_iosurface.borrow().as_ref().map(|surface| surface.identity)) - } - - /// Snapshot the just-presented IOSurface and return its mach port - /// name plus dimensions and stable surface id. Increments the - /// IOSurface's mach-port use count; the - /// receiving process holds it via `IOSurfaceLookupFromMachPort` and - /// is responsible for `mach_port_deallocate` once the import is - /// finished. - pub fn current_iosurface_mach_port(&self) -> Result { - let presented = self.last_presented_iosurface.borrow(); - let presented = presented.as_ref().ok_or(SurfmanError::Failed)?; - let mach_port = presented.native.0.create_mach_port(); - Ok(IOSurfaceHandle { - mach_port_name: mach_port, - surface_id: presented.identity.surface_id, - width: presented.identity.width, - height: presented.identity.height, - }) - } - - fn capture_presented_iosurface(&self, device: &mut Device) { - let Some(surface) = self.swap_chain.take_pending_surface() else { - self.last_presented_iosurface.borrow_mut().take(); - return; - }; - let info = device.surface_info(&surface); - let native = device.native_surface(&surface); - let identity = IOSurfaceIdentity { - surface_id: info.id.0 as u64, - width: u32::try_from(info.size.width).unwrap_or(0), - height: u32::try_from(info.size.height).unwrap_or(0), - }; - self.held_presented_surface.replace(Some(surface)); - self.last_presented_iosurface.replace(Some(PresentedIOSurface { identity, native })); - } - - fn recycle_held_presented_surface(&self) { - if let Some(surface) = self.held_presented_surface.borrow_mut().take() { - self.swap_chain.recycle_surface(surface); - } - } - - fn destroy_held_presented_surface(&self, device: &mut Device, context: &mut Context) { - self.last_presented_iosurface.borrow_mut().take(); - if let Some(mut surface) = self.held_presented_surface.borrow_mut().take() { - let _ = device.destroy_surface(context, &mut surface); - } - } -} - -#[cfg(target_os = "macos")] -struct PresentedIOSurface { - identity: IOSurfaceIdentity, - native: NativeSurface, -} - -/// Trimmed mirror of `paint_api::rendering_context::SurfmanRenderingContext`. -/// -/// Only the methods the public type above actually uses are kept; the -/// upstream original also wires up texture sharing, refresh drivers, -/// and several other knobs that Servo's compositor reaches into but -/// the embedder's headless readback path does not. -struct SurfmanInner { - gleam_gl: Rc, - glow_gl: Arc, - device: RefCell, - context: RefCell, -} - -impl Drop for SurfmanInner { - fn drop(&mut self) { - let device = &mut self.device.borrow_mut(); - let context = &mut self.context.borrow_mut(); - let _ = device.destroy_context(context); - } -} - -impl SurfmanInner { - fn new(connection: &Connection, adapter: &surfman::Adapter) -> Result { - let device = connection.create_device(adapter)?; - - let flags = ContextAttributeFlags::ALPHA - | ContextAttributeFlags::DEPTH - | ContextAttributeFlags::STENCIL; - let gl_api = connection.gl_api(); - let version = match &gl_api { - GLApi::GLES => surfman::GLVersion { major: 3, minor: 0 }, - GLApi::GL => surfman::GLVersion { major: 3, minor: 2 }, - }; - let context_descriptor = - device.create_context_descriptor(&ContextAttributes { flags, version })?; - let context = device.create_context(&context_descriptor, None)?; - - // Loading the GL function pointers requires unsafe ABI calls - // through surfman's `get_proc_address` — these are the same - // calls the upstream `SurfmanRenderingContext::new` makes, - // and they're sound for the same reason: surfman guarantees - // the returned function pointers match the requested API. - #[expect(unsafe_code)] - let gleam_gl = { - match gl_api { - GLApi::GL => unsafe { - gl::GlFns::load_with(|name| device.get_proc_address(&context, name)) - }, - GLApi::GLES => unsafe { - gl::GlesFns::load_with(|name| device.get_proc_address(&context, name)) - }, - } - }; - - #[expect(unsafe_code)] - let glow_gl = unsafe { - glow::Context::from_loader_function(|name| device.get_proc_address(&context, name)) - }; - - Ok(Self { - gleam_gl, - glow_gl: Arc::new(glow_gl), - device: RefCell::new(device), - context: RefCell::new(context), - }) - } - - fn create_surface( - &self, - surface_type: SurfaceType, - ) -> Result { - let device = &mut self.device.borrow_mut(); - let context = &self.context.borrow(); - device.create_surface(context, SurfaceAccess::GPUOnly, surface_type) - } - - fn bind_surface(&self, surface: Surface) -> Result<(), SurfmanError> { - let device = &self.device.borrow(); - let context = &mut self.context.borrow_mut(); - device.bind_surface_to_context(context, surface).map_err(|(err, mut surface)| { - let _ = device.destroy_surface(context, &mut surface); - err - })?; - Ok(()) - } - - fn create_attached_swap_chain(&self) -> Result, SurfmanError> { - let device = &mut self.device.borrow_mut(); - let context = &mut self.context.borrow_mut(); - SwapChain::create_attached(device, context, SurfaceAccess::GPUOnly) - } - - fn make_current(&self) -> Result<(), SurfmanError> { - let device = &self.device.borrow(); - let context = &self.context.borrow(); - device.make_context_current(context) - } - - fn framebuffer_id(&self) -> u32 { - let device = &self.device.borrow(); - let context = &self.context.borrow(); - device - .context_surface_info(context) - .unwrap_or(None) - .and_then(|info| info.framebuffer_object) - .map_or(0, |framebuffer| framebuffer.0.into()) - } - - fn prepare_for_rendering(&self) { - let framebuffer_id = self.framebuffer_id(); - self.gleam_gl.bind_framebuffer(gleam::gl::FRAMEBUFFER, framebuffer_id); - } - - /// Inlined copy of `Framebuffer::read_framebuffer_to_image` from - /// `paint-api`. Reads the bound framebuffer into a `Vec`, - /// flips it vertically (GL's origin is bottom-left, the rest of - /// the embedder expects top-left), and returns it as an - /// [`RgbaImage`]. Returns `None` if `RgbaImage::from_raw` rejects - /// the buffer (size mismatch); GL errors are logged but don't - /// abort the read — the caller can decide whether a corrupt - /// frame is recoverable. - fn read_to_image(&self, source_rectangle: DeviceIntRect) -> Option { - let framebuffer_id = self.framebuffer_id(); - self.gleam_gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id); - // Working around an OSMesa headless bug carried forward from - // the upstream implementation, see servo/servo#18606. - self.gleam_gl.bind_vertex_array(0); - - let mut pixels = self.gleam_gl.read_pixels( - source_rectangle.min.x, - source_rectangle.min.y, - source_rectangle.width(), - source_rectangle.height(), - gl::RGBA, - gl::UNSIGNED_BYTE, - ); - let gl_error = self.gleam_gl.get_error(); - if gl_error != gl::NO_ERROR { - log::warn!("GL error 0x{gl_error:x} after read_pixels in hardware offscreen context"); - } - - let source_rectangle = source_rectangle.to_usize(); - let stride = source_rectangle.width().checked_mul(4)?; - let mirror = pixels.clone(); - for y in 0..source_rectangle.height() { - let dst_start = y.checked_mul(stride)?; - let src_start = (source_rectangle.height().checked_sub(y + 1)?).checked_mul(stride)?; - let dst_end = dst_start.checked_add(stride)?; - let src_end = src_start.checked_add(stride)?; - if dst_end > pixels.len() || src_end > mirror.len() { - return None; - } - pixels[dst_start..dst_end].clone_from_slice(&mirror[src_start..src_end]); - } - - RgbaImage::from_raw( - source_rectangle.width() as u32, - source_rectangle.height() as u32, - pixels, - ) - } -} diff --git a/crates/ely_servo_host/src/host.rs b/crates/ely_servo_host/src/host.rs index 6878213..11442c8 100644 --- a/crates/ely_servo_host/src/host.rs +++ b/crates/ely_servo_host/src/host.rs @@ -284,11 +284,6 @@ pub struct KeyboardTextRequest { pub text: String, } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ScreenshotRequest { - pub webview_id: WebViewId, -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct PermissionRequest { pub webview_id: WebViewId, @@ -341,11 +336,6 @@ pub trait ServoHost { fn type_text(&mut self, request: KeyboardTextRequest) -> Result<(), ServoHostError>; - fn capture_screenshot( - &mut self, - request: ScreenshotRequest, - ) -> Result; - fn set_permission( &mut self, request: PermissionRequest, diff --git a/crates/ely_servo_host/src/iosurface_handle.rs b/crates/ely_servo_host/src/iosurface_handle.rs deleted file mode 100644 index c6647fa..0000000 --- a/crates/ely_servo_host/src/iosurface_handle.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Cross-process IOSurface descriptor types. -//! -//! These wire types live outside `hardware_rendering_context` (which -//! is hardware-render + macOS gated) so the sidecar's JSON protocol -//! can carry an `Option` regardless of feature -//! flags. The receiver always knows how to parse the field; if no -//! sender ever populates it (software-only build), it's just `None` -//! on every frame. -//! -//! Minting an [`IOSurfaceHandle`] requires a hardware surfman context -//! and a macOS host. That part lives in -//! [`crate::hardware_rendering_context`]. - -/// Cross-process handle to a hardware surface: the receiving process -/// rebuilds an `IOSurfaceRef` from `mach_port_name` and imports it as -/// a Metal texture without copying pixels. -/// -/// `surface_id` is the stable surfman `SurfaceID` (a pointer-shaped -/// `usize` widened to `u64` for the wire). Together with `width` and -/// `height` it lets the receiver dedup imported IOSurfaces. The pixel -/// dimensions are part of the identity because a resize can reuse the -/// same surfman id for a newly-sized IOSurface. `width` and `height` -/// are reported in surface pixels (post-DPR). -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "servo-engine", derive(serde::Serialize, serde::Deserialize))] -pub struct IOSurfaceHandle { - pub mach_port_name: u32, - pub surface_id: u64, - pub width: u32, - pub height: u32, -} - -/// Identity-only peek of the currently bound IOSurface. Distinguishes -/// "same surface as last frame" from "resize/swap rotated to a new -/// surface" without minting a fresh mach port (mach ports are a scarce -/// kernel resource and `IOSurfaceCreateMachPort` is not cheap). -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct IOSurfaceIdentity { - pub surface_id: u64, - pub width: u32, - pub height: u32, -} - -impl IOSurfaceIdentity { - pub fn from_handle(handle: IOSurfaceHandle) -> Self { - Self { surface_id: handle.surface_id, width: handle.width, height: handle.height } - } -} diff --git a/crates/ely_servo_host/src/lib.rs b/crates/ely_servo_host/src/lib.rs index 5bec4e1..da1b0fc 100644 --- a/crates/ely_servo_host/src/lib.rs +++ b/crates/ely_servo_host/src/lib.rs @@ -1,8 +1,5 @@ mod error; -#[cfg(feature = "hardware-render")] -mod hardware_rendering_context; mod host; -mod iosurface_handle; #[cfg(feature = "servo-engine")] mod keyboard; #[cfg(feature = "servo-engine")] @@ -17,14 +14,11 @@ mod runtime_waker; mod runtime_webview; pub use error::ServoHostError; -#[cfg(feature = "hardware-render")] -pub use hardware_rendering_context::HardwareOffscreenContext; pub use host::{ HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseDragRequest, MouseHoverRequest, NavigationRequest, PageZoomRequest, PermissionDecision, PermissionRequest, RenderedFrame, - RenderedFrameSummary, ResizeRequest, ScreenshotRequest, ScrollRequest, ServoHost, - TouchTapRequest, WebViewSnapshot, WebViewState, + RenderedFrameSummary, ResizeRequest, ScrollRequest, ServoHost, TouchTapRequest, + WebViewSnapshot, WebViewState, }; -pub use iosurface_handle::{IOSurfaceHandle, IOSurfaceIdentity}; #[cfg(feature = "servo-engine")] pub use runtime::{RenderingContextKind, ServoSurfaceSize, SoftwareServoHost}; diff --git a/crates/ely_servo_host/src/runtime.rs b/crates/ely_servo_host/src/runtime.rs index b57c43a..fe43262 100644 --- a/crates/ely_servo_host/src/runtime.rs +++ b/crates/ely_servo_host/src/runtime.rs @@ -4,18 +4,17 @@ use std::{ path::PathBuf, rc::Rc, sync::{ - Arc, + Arc, Once, atomic::{AtomicBool, Ordering}, }, - thread, - time::{Duration, Instant}, }; use dpi::PhysicalSize; use ely_domain::{ProfileId, TabId, WebViewId}; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use servo::{ - DevicePoint, DeviceVector2D, Opts, Scroll, Servo, ServoBuilder, WebViewBuilder, WebViewPoint, - WebViewVector, + DevicePoint, DeviceVector2D, Opts, Preferences, Scroll, Servo, ServoBuilder, WebViewBuilder, + WebViewPoint, WebViewVector, }; #[path = "runtime_context.rs"] @@ -28,8 +27,8 @@ use url::Url; use crate::{ HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseDragRequest, MouseHoverRequest, NavigationRequest, PageZoomRequest, PermissionDecision, PermissionRequest, RenderedFrame, - ResizeRequest, ScreenshotRequest, ScrollRequest, ServoHost, ServoHostError, TouchTapRequest, - WebViewSnapshot, WebViewState, + ResizeRequest, ScrollRequest, ServoHost, ServoHostError, TouchTapRequest, WebViewSnapshot, + WebViewState, runtime_input::{ send_keyboard_text, send_mouse_click, send_mouse_drag, send_mouse_hover, send_touch_tap, }, @@ -39,8 +38,7 @@ use crate::{ }; static SERVO_RUNTIME_STARTED: AtomicBool = AtomicBool::new(false); -const SCREENSHOT_TIMEOUT: Duration = Duration::from_secs(20); -const SCREENSHOT_POLL_INTERVAL: Duration = Duration::from_millis(2); +static RUSTLS_PROVIDER: Once = Once::new(); pub struct SoftwareServoHost { servo: Servo, @@ -70,12 +68,6 @@ impl SoftwareServoHost { config_dir: Option, rendering_context_kind: RenderingContextKind, ) -> Result { - if rendering_context_kind == RenderingContextKind::Hardware - && !cfg!(feature = "hardware-render") - { - return Err(ServoHostError::HardwareRenderUnavailable); - } - if SERVO_RUNTIME_STARTED .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) .is_err() @@ -96,7 +88,22 @@ impl SoftwareServoHost { profile_id: ProfileId, size: ServoSurfaceSize, ) -> Result { - self.create_webview_in_context(tab_id, profile_id, size) + let handles = self.new_rendering_context(size)?; + self.create_webview_in_context(tab_id, profile_id, handles) + } + + pub fn create_webview_with_native_surface( + &mut self, + tab_id: TabId, + profile_id: ProfileId, + size: ServoSurfaceSize, + native_surface: &S, + ) -> Result + where + S: HasDisplayHandle + HasWindowHandle + ?Sized, + { + let handles = self.new_rendering_context_for_native_surface(size, native_surface)?; + self.create_webview_in_context(tab_id, profile_id, handles) } /// Paint and present the current surface without RGBA readback. @@ -121,8 +128,10 @@ impl SoftwareServoHost { config_dir: Option, rendering_context_kind: RenderingContextKind, ) -> Result { + install_rustls_provider(); let wake_requested = Arc::new(AtomicBool::new(false)); let mut builder = ServoBuilder::default() + .preferences(ely_servo_preferences()) .event_loop_waker(Box::new(ServoWakeFlag::new(wake_requested.clone()))); if let Some(config_dir) = config_dir { builder = builder.opts(Opts { config_dir: Some(config_dir), ..Opts::default() }); @@ -183,13 +192,24 @@ impl SoftwareServoHost { } } +fn install_rustls_provider() { + RUSTLS_PROVIDER.call_once(|| { + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + }); +} + +fn ely_servo_preferences() -> Preferences { + Preferences { dom_intersection_observer_enabled: true, ..Preferences::default() } +} + impl ServoHost for SoftwareServoHost { fn create_webview( &mut self, tab_id: TabId, profile_id: ProfileId, ) -> Result { - self.create_webview_in_context(tab_id, profile_id, self.default_surface_size) + let handles = self.new_rendering_context(self.default_surface_size)?; + self.create_webview_in_context(tab_id, profile_id, handles) } fn navigate(&mut self, request: NavigationRequest) -> Result<(), ServoHostError> { @@ -313,41 +333,6 @@ impl ServoHost for SoftwareServoHost { Ok(()) } - fn capture_screenshot( - &mut self, - request: ScreenshotRequest, - ) -> Result { - let webview = self.webview(&request.webview_id)?.webview.clone(); - let captured_image = Rc::new(RefCell::new(None)); - let callback_image = captured_image.clone(); - webview.take_screenshot(None, move |result| { - callback_image.replace(Some(result)); - }); - - let started_at = Instant::now(); - while captured_image.borrow().is_none() { - if started_at.elapsed() >= SCREENSHOT_TIMEOUT { - return Err(ServoHostError::ScreenshotTimedOut { id: request.webview_id.clone() }); - } - - self.tick(); - if self.snapshot(&request.webview_id)?.has_pending_frame() { - self.paint(&request.webview_id)?; - } - thread::sleep(SCREENSHOT_POLL_INTERVAL); - } - - let Some(result) = captured_image.borrow_mut().take() else { - return Err(ServoHostError::RenderedFrameUnavailable); - }; - let image = result.map_err(|error| ServoHostError::ScreenshotUnavailable { - reason: format!("{error:?}"), - })?; - let frame = RenderedFrame::from_rgba_bytes(image.width(), image.height(), image.into_raw()); - self.last_rendered_frame = Some(frame.clone()); - Ok(frame) - } - fn set_permission( &mut self, request: PermissionRequest, @@ -392,15 +377,20 @@ impl ServoHost for SoftwareServoHost { } } +impl Drop for SoftwareServoHost { + fn drop(&mut self) { + SERVO_RUNTIME_STARTED.store(false, Ordering::Release); + } +} + impl SoftwareServoHost { fn create_webview_in_context( &mut self, tab_id: TabId, profile_id: ProfileId, - size: ServoSurfaceSize, + handles: runtime_context::RenderingContextHandles, ) -> Result { let webview_id = WebViewId::new(); - let handles = self.new_rendering_context(size)?; let delegate = Rc::new(HostWebViewDelegate::new(profile_id.clone(), self.permissions.clone())); let webview = WebViewBuilder::new(&self.servo, handles.rendering_context.clone()) @@ -420,8 +410,6 @@ impl SoftwareServoHost { tab_id, profile_id, rendering_context: handles.rendering_context, - #[cfg(feature = "hardware-render")] - hardware_context: handles.hardware_context, webview, delegate, requested_url: None, @@ -431,42 +419,6 @@ impl SoftwareServoHost { Ok(webview_id) } - /// Cheap peek at the IOSurface identity bound to this webview's - /// hardware context. Returns `None` for software webviews and on - /// non-macOS hosts; otherwise the surfman `SurfaceID`-derived - /// identity plus dimensions. Used by the sidecar's live loop to - /// dedup mach port creation. - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - pub fn peek_iosurface_identity( - &self, - webview_id: &WebViewId, - ) -> Result, ServoHostError> { - let webview = self.webview(webview_id)?; - let Some(hardware) = webview.hardware_context.as_ref() else { - return Ok(None); - }; - hardware.peek_iosurface_identity().map_err(|_| ServoHostError::RenderingContextUnavailable) - } - - /// Mint a fresh mach port for the IOSurface bound to this - /// webview's hardware context. The caller is responsible for - /// transferring the port to the receiving process; if no transfer - /// happens, the port leaks. Software webviews return `None`. - #[cfg(all(feature = "hardware-render", target_os = "macos"))] - pub fn current_iosurface_handle( - &self, - webview_id: &WebViewId, - ) -> Result, ServoHostError> { - let webview = self.webview(webview_id)?; - let Some(hardware) = webview.hardware_context.as_ref() else { - return Ok(None); - }; - hardware - .current_iosurface_mach_port() - .map(Some) - .map_err(|_| ServoHostError::RenderingContextUnavailable) - } - fn webview(&self, webview_id: &WebViewId) -> Result<&HostWebView, ServoHostError> { self.webviews .get(webview_id) diff --git a/crates/ely_servo_host/src/runtime_context.rs b/crates/ely_servo_host/src/runtime_context.rs index ceeab56..8845583 100644 --- a/crates/ely_servo_host/src/runtime_context.rs +++ b/crates/ely_servo_host/src/runtime_context.rs @@ -8,6 +8,7 @@ use std::{ use dpi::PhysicalSize; use euclid::Scale; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use servo::{ DeviceIndependentPixel, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel, RenderingContext, @@ -62,31 +63,16 @@ impl ServoSurfaceSize { } /// Selects the `RenderingContext` implementation each webview gets. -/// -/// `Software` uses Servo's built-in `SoftwareRenderingContext`, which -/// rasterises on the CPU. `Hardware` uses the vendored -/// [`HardwareOffscreenContext`](crate::HardwareOffscreenContext), -/// which rasterises through the real GPU adapter against a -/// `SurfaceType::Generic` offscreen surface. The `Hardware` variant -/// is only available when the `hardware-render` feature is enabled; -/// requesting it without the feature is a configuration error -/// surfaced via `ServoHostError::HardwareRenderUnavailable`. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum RenderingContextKind { #[default] Software, - Hardware, } /// Pair of rendering-context handles produced by -/// [`SoftwareServoHost::new_rendering_context`]. The trait-object -/// handle drives Servo's compositor; the concrete hardware handle is -/// kept on the side so the host can call macOS-specific methods -/// (IOSurface mach port extraction) without downcasting. +/// [`SoftwareServoHost::new_rendering_context`]. pub(super) struct RenderingContextHandles { pub(super) rendering_context: Rc, - #[cfg(feature = "hardware-render")] - pub(super) hardware_context: Option>, } impl SoftwareServoHost { @@ -103,29 +89,33 @@ impl SoftwareServoHost { rendering_context .make_current() .map_err(|_| ServoHostError::RenderingContextNotCurrent)?; - Ok(RenderingContextHandles { - rendering_context, - #[cfg(feature = "hardware-render")] - hardware_context: None, - }) + Ok(RenderingContextHandles { rendering_context }) } - #[cfg(feature = "hardware-render")] - RenderingContextKind::Hardware => { - let hardware = Rc::new( - crate::HardwareOffscreenContext::new(size.physical()) - .map_err(|_| ServoHostError::RenderingContextUnavailable)?, - ); - hardware.make_current().map_err(|_| ServoHostError::RenderingContextNotCurrent)?; - Ok(RenderingContextHandles { - rendering_context: hardware.clone(), - hardware_context: Some(hardware), - }) - } - #[cfg(not(feature = "hardware-render"))] - RenderingContextKind::Hardware => Err(ServoHostError::HardwareRenderUnavailable), } } + pub(super) fn new_rendering_context_for_native_surface( + &self, + size: ServoSurfaceSize, + native_surface: &S, + ) -> Result + where + S: HasDisplayHandle + HasWindowHandle + ?Sized, + { + let display_handle = native_surface + .display_handle() + .map_err(|_| ServoHostError::RenderingContextUnavailable)?; + let window_handle = native_surface + .window_handle() + .map_err(|_| ServoHostError::RenderingContextUnavailable)?; + let rendering_context = Rc::new( + servo::WindowRenderingContext::new(display_handle, window_handle, size.physical()) + .map_err(|_| ServoHostError::RenderingContextUnavailable)?, + ); + rendering_context.make_current().map_err(|_| ServoHostError::RenderingContextNotCurrent)?; + Ok(RenderingContextHandles { rendering_context }) + } + /// Spin Servo's event loop until the webview's delegate observes a /// fresh `notify_new_frame_ready` callback (i.e. the framebuffer is /// consistent for readback) or [`paint_barrier_budget`] elapses. The diff --git a/crates/ely_servo_host/src/runtime_webview.rs b/crates/ely_servo_host/src/runtime_webview.rs index d9091eb..808cb11 100644 --- a/crates/ely_servo_host/src/runtime_webview.rs +++ b/crates/ely_servo_host/src/runtime_webview.rs @@ -13,12 +13,6 @@ pub(super) struct HostWebView { pub(super) tab_id: TabId, pub(super) profile_id: ProfileId, pub(super) rendering_context: Rc, - /// Parallel concrete handle when the rendering context is the - /// vendored hardware path. `None` for software webviews. Lets the - /// host call macOS-specific methods (IOSurface mach port - /// extraction) without downcasting `dyn RenderingContext`. - #[cfg(feature = "hardware-render")] - pub(super) hardware_context: Option>, pub(super) webview: WebView, pub(super) delegate: Rc, pub(super) requested_url: Option, diff --git a/crates/ely_servo_host/tests/hardware_rendering_context.rs b/crates/ely_servo_host/tests/hardware_rendering_context.rs deleted file mode 100644 index 9a6e164..0000000 --- a/crates/ely_servo_host/tests/hardware_rendering_context.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Smoke test for the vendored hardware [`RenderingContext`]. -//! -//! Runs only when the `hardware-render` feature is enabled. The test -//! degrades gracefully when the host machine lacks a hardware GL -//! adapter (CI sandboxes, no-GPU containers): construction returns -//! `Err`, the test logs the cause, and reports `ok` — proving the -//! vendored constructor is wired up correctly without falsely -//! marking the suite green when a GPU is actually expected and -//! missing. Inverting that check (turning a GPU-missing host into a -//! hard failure) is left for downstream CI configuration once the -//! hardware path is wired into the sidecar binary. - -#![cfg(feature = "hardware-render")] - -use dpi::PhysicalSize; -use ely_servo_host::HardwareOffscreenContext; -use servo::RenderingContext; - -#[test] -fn constructs_or_explains_why_not() { - let size = PhysicalSize::new(640, 480); - match HardwareOffscreenContext::new(size) { - Ok(context) => { - // We don't drive Servo here — just confirm the vendored - // glue produced a live context. Construction is the - // failure mode this smoke test guards against; once a - // context exists the real Servo paint path exercises the - // rest of the surface. - drop(context); - } - Err(error) => { - eprintln!( - "hardware GL adapter not available on this host \ - (acceptable in headless / no-GPU environments): {error:?}" - ); - } - } -} - -#[cfg(target_os = "macos")] -#[test] -fn extracts_iosurface_mach_port_from_current_surface() -> Result<(), String> { - let width = 256; - let height = 192; - let context = match HardwareOffscreenContext::new(PhysicalSize::new(width, height)) { - Ok(context) => context, - Err(error) => { - eprintln!( - "hardware GL adapter not available on this host \ - (acceptable in headless / no-GPU environments): {error:?}" - ); - return Ok(()); - } - }; - - context.prepare_for_rendering(); - context.present(); - let first = context - .current_iosurface_mach_port() - .map_err(|error| format!("first IOSurface mach port extraction failed: {error:?}"))?; - assert!( - first.mach_port_name != 0, - "IOSurfaceCreateMachPort must return a non-null mach_port_t (got 0)" - ); - assert_eq!(first.width, width, "reported width must match surface width"); - assert_eq!(first.height, height, "reported height must match surface height"); - - // The unbind/rebind cycle must leave the context usable: a second - // call should still produce a valid mach port without panicking on - // a stale `Framebuffer::None`. - let second = context - .current_iosurface_mach_port() - .map_err(|error| format!("repeated mach port extraction failed: {error:?}"))?; - assert!(second.mach_port_name != 0); - assert_eq!(second.width, width); - assert_eq!(second.height, height); - Ok(()) -} diff --git a/crates/ely_servo_host/tests/live_perf_bench.rs b/crates/ely_servo_host/tests/live_perf_bench.rs deleted file mode 100644 index 457e41c..0000000 --- a/crates/ely_servo_host/tests/live_perf_bench.rs +++ /dev/null @@ -1,494 +0,0 @@ -//! Manual sidecar perf bench; ignored by normal CI. -#![cfg(feature = "servo-engine")] - -#[path = "live_perf_bench/pixels.rs"] -mod pixels; - -use std::{ - env, - error::Error, - fs, - io::{BufRead, BufReader, Read, Write}, - path::PathBuf, - process::{Child, ChildStdin, ChildStdout, Command, Stdio}, - thread, - time::{Duration, Instant}, -}; - -use ely_domain::{ProfileId, TabId}; -use serde::Deserialize; - -const DEFAULT_FRAMES: u32 = 240; -const VIEWPORT_WIDTH: u32 = 1024; -const VIEWPORT_HEIGHT: u32 = 768; -const SCROLL_STEP_PX: i32 = 4; -const RESPONSE_TIMEOUT: Duration = Duration::from_secs(20); -const SCROLL_PAGE_DATA_URL: &str = "data:text/html,\ -perf\ -\ -"; - -#[derive(Deserialize, Debug)] -struct LiveResponse { - error: Option, - frame: Option, - #[serde(default)] - perf: Option, - #[serde(default)] - surface_handle: Option, - #[serde(default)] - current_surface_id: Option, -} - -#[derive(Deserialize, Debug, Clone, Copy)] -struct BenchSurfaceHandle { - mach_port_name: u32, - surface_id: u64, - width: u32, - height: u32, -} - -#[derive(Deserialize, Debug)] -struct LiveFrameReport { - rgba_byte_count: usize, - #[serde(default)] - width: u32, - #[serde(default)] - height: u32, - #[serde(default)] - device_pixel_ratio: f32, - #[serde(default)] - css_viewport_width: u32, - #[serde(default)] - css_viewport_height: u32, -} - -#[derive(Deserialize, Debug, Clone)] -struct FramePerfSummary { - window: u32, - context: String, - paint_p50_us: u64, - paint_p95_us: u64, - paint_p99_us: u64, - encode_p50_us: u64, - encode_p95_us: u64, - encode_p99_us: u64, - write_p50_us: u64, - write_p95_us: u64, - write_p99_us: u64, - total_p50_us: u64, - total_p95_us: u64, - total_p99_us: u64, -} - -#[test] -#[ignore = "manual bench: spawns sidecar, scrolls a data: URL for N frames"] -fn run_live_bench() -> Result<(), Box> { - let kind = env::var("ELY_PERF_KIND").unwrap_or_else(|_| "software".to_string()); - let frames: u32 = env::var("ELY_PERF_FRAMES") - .ok() - .and_then(|value| value.parse().ok()) - .unwrap_or(DEFAULT_FRAMES); - let url = env::var("ELY_PERF_URL").unwrap_or_else(|_| SCROLL_PAGE_DATA_URL.to_string()); - - let profile_id = ProfileId::new(); - let tab = TabId::new(); - let profile_data_dir = env::temp_dir().join(format!( - "ely-perf-bench-{}-{}-{}", - std::process::id(), - kind, - profile_id.as_str() - )); - fs::create_dir_all(&profile_data_dir)?; - - let mut child = spawn_sidecar(&kind, &profile_data_dir)?; - let mut stdin = child.stdin.take().ok_or("sidecar stdin missing")?; - let stdout = child.stdout.take().ok_or("sidecar stdout missing")?; - let mut reader = BufReader::new(stdout); - - let outcome = match drive_bench(&mut stdin, &mut reader, &kind, &tab, &profile_id, &url, frames) - { - Ok(outcome) => outcome, - Err(error) => { - drop(stdin); - let _ = child.kill(); - cleanup(&profile_data_dir)?; - return Err(error); - } - }; - - drop(stdin); - let _ = child.wait(); - cleanup(&profile_data_dir)?; - - print_summaries(&kind, frames, &outcome.summaries); - print_surface_handles(&kind, &outcome.surface_handles); - print_current_surface_summary(&kind, &outcome.current_surface_ids); - eprintln!( - "\n=== ELY_PERF_KIND={kind} readback_rgba_bytes={} surface_rgba_bytes={} ===", - outcome.readback_rgba_bytes, outcome.surface_rgba_bytes, - ); - assert!( - !outcome.summaries.is_empty(), - "expected at least one FramePerfSummary across {frames} frames" - ); - if kind == "hardware" { - assert!( - !outcome.surface_handles.is_empty(), - "hardware live path must publish IOSurface handles" - ); - assert!( - !outcome.current_surface_ids.is_empty(), - "hardware live path must report current_surface_id selectors" - ); - } else { - assert!( - outcome.surface_handles.is_empty(), - "software path must never publish an IOSurface handle" - ); - assert!( - outcome.current_surface_ids.is_empty(), - "software path must never report current_surface_id" - ); - } - let viewport_bytes = (1024u64) * (768u64) * 4; - let total_rgba_bytes = outcome.readback_rgba_bytes + outcome.surface_rgba_bytes; - assert!( - total_rgba_bytes >= viewport_bytes, - "{kind} path delivered only {total_rgba_bytes} bytes — expected at least one full frame ({})", - viewport_bytes, - ); - if kind == "hardware" { - let full_readback_budget = viewport_bytes * u64::from(frames); - assert_eq!( - outcome.readback_rgba_bytes, viewport_bytes, - "hardware path should read back only the initial visible frame" - ); - assert!( - total_rgba_bytes < full_readback_budget, - "hardware path stayed on full readback: {total_rgba_bytes} >= {full_readback_budget}" - ); - } - Ok(()) -} - -struct BenchOutcome { - summaries: Vec, - surface_handles: Vec, - current_surface_ids: Vec, - readback_rgba_bytes: u64, - surface_rgba_bytes: u64, -} - -fn spawn_sidecar(kind: &str, profile_data_dir: &PathBuf) -> Result> { - let mut command = Command::new(env!("CARGO_BIN_EXE_ely_servo_sidecar")); - command - .arg("live") - .arg("--profile-data-dir") - .arg(profile_data_dir) - .arg("--rendering-context") - .arg(kind) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()); - Ok(command.spawn()?) -} - -fn drive_bench( - stdin: &mut ChildStdin, - reader: &mut BufReader, - kind: &str, - tab: &TabId, - profile_id: &ProfileId, - url: &str, - frames: u32, -) -> Result> { - let mut summaries = Vec::new(); - let mut surface_handles = Vec::new(); - let mut current_surface_ids = Vec::new(); - let mut readback_rgba_bytes: u64 = 0; - let mut surface_rgba_bytes: u64 = 0; - - let navigate = build_ensure(tab, profile_id, url, 0, 0, false); - write_request(stdin, &navigate)?; - let response = read_response(reader, RESPONSE_TIMEOUT)?; - assert_frame_viewport_report(&response); - record_summary(&response, kind, &mut summaries); - record_surface_handle(&response, kind, &mut surface_handles); - record_current_surface_id(&response, &mut current_surface_ids); - record_rgba_bytes(&response, &mut readback_rgba_bytes, &mut surface_rgba_bytes); - - let mut accumulated_scroll = 0; - let mut painted_frames = 0; - let max_attempts = frames.saturating_mul(4).max(frames + 10); - for attempt in 0..max_attempts { - if painted_frames >= frames { - break; - } - let scroll_delta_y = - if painted_frames % 80 == 79 { -SCROLL_STEP_PX * 60 } else { SCROLL_STEP_PX }; - accumulated_scroll += scroll_delta_y; - let request = build_ensure(tab, profile_id, url, 0, scroll_delta_y, true); - write_request(stdin, &request)?; - let response = read_response(reader, RESPONSE_TIMEOUT)?; - if let Some(error) = response.error.as_ref() { - return Err(format!("sidecar error at attempt {attempt}: {error}").into()); - } - if response.frame.is_some() { - painted_frames += 1; - } - assert_frame_viewport_report(&response); - record_summary(&response, kind, &mut summaries); - record_surface_handle(&response, kind, &mut surface_handles); - record_current_surface_id(&response, &mut current_surface_ids); - record_rgba_bytes(&response, &mut readback_rgba_bytes, &mut surface_rgba_bytes); - } - assert_eq!(painted_frames, frames, "bench did not receive the requested painted frame count"); - let _ = accumulated_scroll; - for _ in 0..5 { - if !summaries.is_empty() { - break; - } - let poll = build_poll(tab); - write_request(stdin, &poll)?; - let response = read_response(reader, RESPONSE_TIMEOUT)?; - record_summary(&response, kind, &mut summaries); - } - - Ok(BenchOutcome { - summaries, - surface_handles, - current_surface_ids, - readback_rgba_bytes, - surface_rgba_bytes, - }) -} - -fn record_rgba_bytes( - response: &LiveResponse, - readback_rgba_bytes: &mut u64, - surface_rgba_bytes: &mut u64, -) { - let rgba_byte_count = response.frame.as_ref().map_or(0, |frame| frame.rgba_byte_count as u64); - if rgba_byte_count > 0 { - *readback_rgba_bytes += rgba_byte_count; - } else if response.current_surface_id.is_some() { - *surface_rgba_bytes += rgba_byte_count; - } -} - -fn assert_frame_viewport_report(response: &LiveResponse) { - let Some(frame) = response.frame.as_ref() else { - return; - }; - let dpr = if frame.device_pixel_ratio.is_finite() && frame.device_pixel_ratio > 0.0 { - frame.device_pixel_ratio - } else { - 1.0 - }; - let expected_width = ((frame.width as f32) / dpr).round().max(1.0) as u32; - let expected_height = ((frame.height as f32) / dpr).round().max(1.0) as u32; - - assert_eq!( - frame.css_viewport_width, expected_width, - "CSS viewport width must match physical width divided by DPR", - ); - assert_eq!( - frame.css_viewport_height, expected_height, - "CSS viewport height must match physical height divided by DPR", - ); -} - -fn record_surface_handle( - response: &LiveResponse, - kind: &str, - surface_handles: &mut Vec, -) { - if let Some(handle) = response.surface_handle { - eprintln!( - "[iosurface {kind}] new surface_id=0x{:x} mach_port=0x{:x} {}x{}", - handle.surface_id, handle.mach_port_name, handle.width, handle.height, - ); - surface_handles.push(handle); - } -} - -fn record_current_surface_id(response: &LiveResponse, current_surface_ids: &mut Vec) { - if let Some(surface_id) = response.current_surface_id { - current_surface_ids.push(surface_id); - } -} - -fn print_surface_handles(kind: &str, surface_handles: &[BenchSurfaceHandle]) { - eprintln!( - "\n=== ELY_PERF_KIND={kind} iosurface_imports={} (one per unique surface) ===", - surface_handles.len() - ); - for (index, handle) in surface_handles.iter().enumerate() { - eprintln!( - "{:<4} surface_id=0x{:x} mach_port=0x{:x} {}x{}", - index, handle.surface_id, handle.mach_port_name, handle.width, handle.height, - ); - } -} - -fn print_current_surface_summary(kind: &str, current_surface_ids: &[u64]) { - use std::collections::BTreeMap; - let mut counts: BTreeMap = BTreeMap::new(); - for id in current_surface_ids { - *counts.entry(*id).or_default() += 1; - } - eprintln!("\n=== ELY_PERF_KIND={kind} current_surface_id histogram (per-frame selector) ===",); - for (surface_id, count) in counts.iter() { - eprintln!("surface_id=0x{:x} frames={}", surface_id, count); - } -} - -fn build_ensure( - tab: &TabId, - profile_id: &ProfileId, - url: &str, - scroll_dx: i32, - scroll_dy: i32, - include_hover: bool, -) -> String { - let hover_x = if include_hover { Some(256u32) } else { None }; - let hover_y = if include_hover { Some(256u32) } else { None }; - let scroll_point = if scroll_dx != 0 || scroll_dy != 0 { Some((256u32, 256u32)) } else { None }; - let hover_x_json = match hover_x { - Some(value) => format!("{value}"), - None => "null".to_string(), - }; - let hover_y_json = match hover_y { - Some(value) => format!("{value}"), - None => "null".to_string(), - }; - let scroll_point_x_json = match scroll_point { - Some((x, _)) => format!("{x}"), - None => "null".to_string(), - }; - let scroll_point_y_json = match scroll_point { - Some((_, y)) => format!("{y}"), - None => "null".to_string(), - }; - format!( - r#"{{"type":"ensure","tab_id":"{tab}","profile_id":"{profile}","url":{url},"width":{w},"height":{h},"page_zoom_percent":100,"scroll_delta_x":{dx},"scroll_delta_y":{dy},"scroll_point_x":{sx},"scroll_point_y":{sy},"click_x":null,"click_y":null,"hover_x":{hx},"hover_y":{hy},"typed_text":null,"site_permissions":[]}}"#, - tab = tab.as_str(), - profile = profile_id.as_str(), - url = serde_json::to_string(url).unwrap_or_else(|_| "\"\"".to_string()), - w = VIEWPORT_WIDTH, - h = VIEWPORT_HEIGHT, - dx = scroll_dx, - dy = scroll_dy, - sx = scroll_point_x_json, - sy = scroll_point_y_json, - hx = hover_x_json, - hy = hover_y_json, - ) -} - -fn build_poll(tab: &TabId) -> String { - format!(r#"{{"type":"poll","tab_id":"{}"}}"#, tab.as_str()) -} - -fn write_request(stdin: &mut ChildStdin, request: &str) -> Result<(), Box> { - stdin.write_all(request.as_bytes())?; - stdin.write_all(b"\n")?; - stdin.flush()?; - Ok(()) -} - -fn read_response( - reader: &mut BufReader, - timeout: Duration, -) -> Result> { - Ok(read_response_with_bytes(reader, timeout)?.0) -} - -fn read_response_with_bytes( - reader: &mut BufReader, - timeout: Duration, -) -> Result<(LiveResponse, Vec), Box> { - let started_at = Instant::now(); - let mut json_line = String::new(); - loop { - json_line.clear(); - let read_bytes = reader.read_line(&mut json_line)?; - if read_bytes == 0 { - return Err("sidecar closed stdout".into()); - } - if json_line.trim().is_empty() { - if started_at.elapsed() >= timeout { - return Err("sidecar response timeout".into()); - } - thread::sleep(Duration::from_millis(2)); - continue; - } - break; - } - let response: LiveResponse = serde_json::from_str(json_line.trim_end())?; - let mut rgba = Vec::new(); - if let Some(frame) = response.frame.as_ref() - && frame.rgba_byte_count > 0 - { - rgba.resize(frame.rgba_byte_count, 0); - reader.read_exact(&mut rgba)?; - } - Ok((response, rgba)) -} - -fn record_summary(response: &LiveResponse, kind: &str, summaries: &mut Vec) { - if let Some(perf) = response.perf.as_ref() { - assert_eq!(perf.context, kind, "sidecar context label must match requested kind"); - eprintln!( - "[perf {kind}] window={} paint p50/p95/p99={}/{}/{} encode {}/{}/{} write {}/{}/{} total {}/{}/{} (µs)", - perf.window, - perf.paint_p50_us, - perf.paint_p95_us, - perf.paint_p99_us, - perf.encode_p50_us, - perf.encode_p95_us, - perf.encode_p99_us, - perf.write_p50_us, - perf.write_p95_us, - perf.write_p99_us, - perf.total_p50_us, - perf.total_p95_us, - perf.total_p99_us, - ); - summaries.push(perf.clone()); - } -} - -fn print_summaries(kind: &str, frames: u32, summaries: &[FramePerfSummary]) { - eprintln!("\n=== ELY_PERF_KIND={kind} frames={frames} windows={} ===", summaries.len()); - for summary in summaries { - eprintln!( - "win={} paint={}/{}/{} encode={}/{}/{} write={}/{}/{} total={}/{}/{}", - summary.window, - summary.paint_p50_us, - summary.paint_p95_us, - summary.paint_p99_us, - summary.encode_p50_us, - summary.encode_p95_us, - summary.encode_p99_us, - summary.write_p50_us, - summary.write_p95_us, - summary.write_p99_us, - summary.total_p50_us, - summary.total_p95_us, - summary.total_p99_us, - ); - } -} - -fn cleanup(profile_data_dir: &PathBuf) -> Result<(), Box> { - match fs::remove_dir_all(profile_data_dir) { - Ok(()) => Ok(()), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()), - Err(error) => Err(error.into()), - } -} diff --git a/crates/ely_servo_host/tests/live_perf_bench/pixels.rs b/crates/ely_servo_host/tests/live_perf_bench/pixels.rs deleted file mode 100644 index 33ad1e8..0000000 --- a/crates/ely_servo_host/tests/live_perf_bench/pixels.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::{ - env, - error::Error, - fs, - io::BufReader, - process::{ChildStdin, ChildStdout}, -}; - -use ely_domain::{ProfileId, TabId}; - -use super::{ - RESPONSE_TIMEOUT, build_ensure, cleanup, read_response_with_bytes, spawn_sidecar, write_request, -}; - -const SOLID_RED_DATA_URL: &str = - "data:text/html,"; -const SOLID_BLUE_DATA_URL: &str = - "data:text/html,"; - -#[test] -#[ignore = "drives a real sidecar via stdin/stdout; takes a few seconds"] -fn red_data_url_yields_red_rgba() -> Result<(), Box> { - assert_solid_color_renders("software", SOLID_RED_DATA_URL, ColorTarget::Red) -} - -#[test] -#[ignore = "drives a real sidecar via stdin/stdout; takes a few seconds"] -fn blue_data_url_yields_blue_rgba() -> Result<(), Box> { - assert_solid_color_renders("software", SOLID_BLUE_DATA_URL, ColorTarget::Blue) -} - -#[derive(Clone, Copy)] -enum ColorTarget { - Red, - Blue, -} - -impl ColorTarget { - fn label(self) -> &'static str { - match self { - ColorTarget::Red => "red", - ColorTarget::Blue => "blue", - } - } -} - -fn assert_solid_color_renders( - kind: &str, - url: &str, - target: ColorTarget, -) -> Result<(), Box> { - let profile_id = ProfileId::new(); - let tab = TabId::new(); - let profile_data_dir = env::temp_dir().join(format!( - "ely-pixel-{}-{}-{}", - std::process::id(), - target.label(), - profile_id.as_str(), - )); - fs::create_dir_all(&profile_data_dir)?; - - let mut child = spawn_sidecar(kind, &profile_data_dir)?; - let mut stdin = child.stdin.take().ok_or("sidecar stdin missing")?; - let stdout = child.stdout.take().ok_or("sidecar stdout missing")?; - let mut reader = BufReader::new(stdout); - let outcome = drive_solid_color_render(&mut stdin, &mut reader, &tab, &profile_id, url, target); - - drop(stdin); - let _ = child.wait(); - cleanup(&profile_data_dir)?; - outcome -} - -fn drive_solid_color_render( - stdin: &mut ChildStdin, - reader: &mut BufReader, - tab: &TabId, - profile_id: &ProfileId, - url: &str, - target: ColorTarget, -) -> Result<(), Box> { - let mut bytes = Vec::new(); - let mut report = None; - for iteration in 0..30 { - let scroll_y = if iteration == 0 { - 0 - } else if iteration % 2 == 1 { - 1 - } else { - -1 - }; - let request = build_ensure(tab, profile_id, url, 0, scroll_y, false); - write_request(stdin, &request)?; - let (response, response_bytes) = read_response_with_bytes(reader, RESPONSE_TIMEOUT)?; - if let Some(error) = response.error.as_ref() { - return Err(format!("sidecar error: {error}").into()); - } - if let Some(frame_report) = response.frame { - if !response_bytes.is_empty() - && sample_matches_target( - &response_bytes, - frame_report.width, - frame_report.height, - target, - ) - { - report = Some(frame_report); - bytes = response_bytes; - break; - } - if !response_bytes.is_empty() { - bytes = response_bytes; - report = Some(frame_report); - } - } - } - - let report = report.ok_or("never received a frame with bytes")?; - let width = report.width as usize; - let height = report.height as usize; - assert_eq!(bytes.len(), width * height * 4, "rgba byte count must match width * height * 4",); - - let mut samples = Vec::new(); - for fy in [1, 2, 3] { - for fx in [1, 2, 3] { - let x = width * fx / 4; - let y = height * fy / 4; - let idx = (y * width + x) * 4; - samples.push((x, y, bytes[idx], bytes[idx + 1], bytes[idx + 2], bytes[idx + 3])); - } - } - eprintln!("[pixel sample {}] {:?}", target.label(), samples); - - let hits = - samples.iter().filter(|(_x, _y, r, g, b, _a)| matches_color(*r, *g, *b, target)).count(); - assert!( - hits >= 5, - "expected at least 5/9 center-quadrant pixels to be {} after rendering {}; got samples {:?}", - target.label(), - url, - samples, - ); - Ok(()) -} - -fn sample_matches_target(bytes: &[u8], width: u32, height: u32, target: ColorTarget) -> bool { - let w = width as usize; - let h = height as usize; - if bytes.len() < w * h * 4 || w == 0 || h == 0 { - return false; - } - let cx = w / 2; - let cy = h / 2; - let idx = (cy * w + cx) * 4; - matches_color(bytes[idx], bytes[idx + 1], bytes[idx + 2], target) -} - -fn matches_color(r: u8, g: u8, b: u8, target: ColorTarget) -> bool { - match target { - ColorTarget::Red => r >= 200 && g <= 60 && b <= 60, - ColorTarget::Blue => r <= 60 && g <= 60 && b >= 200, - } -} diff --git a/crates/ely_servo_host/tests/sidecar.rs b/crates/ely_servo_host/tests/sidecar.rs deleted file mode 100644 index e0554a4..0000000 --- a/crates/ely_servo_host/tests/sidecar.rs +++ /dev/null @@ -1,215 +0,0 @@ -#![cfg(feature = "servo-engine")] - -use std::{collections::BTreeSet, error::Error, fs, path::PathBuf, process::Command}; - -use ely_domain::ProfileId; - -#[path = "sidecar/site_cases.rs"] -mod site_cases; -#[path = "sidecar/support.rs"] -mod support; - -use support::*; - -#[test] -fn sidecar_prd_reference_cases_cover_prd_urls() -> Result<(), Box> { - let prd = fs::read_to_string(prd_path())?; - let prd_urls = prd_reference_urls(&prd); - let covered_urls = PRD_REFERENCE_SITE_COMPATIBILITY_CASES - .iter() - .map(|case| normalized_url(case.url)) - .collect::>(); - let missing_urls = prd_urls - .iter() - .filter(|url| !covered_urls.contains(url.as_str())) - .cloned() - .collect::>(); - - assert!(missing_urls.is_empty(), "missing PRD sidecar smoke cases: {missing_urls:?}"); - assert_eq!(prd_urls.len(), PRD_REFERENCE_SITE_COMPATIBILITY_CASES.len()); - Ok(()) -} - -#[test] -fn sidecar_opens_and_renders_prd_sites_to_rgba_files() -> Result<(), Box> { - for case in PRD_SITE_COMPATIBILITY_CASES { - for size in PRD_SITE_COMPATIBILITY_SIZES { - snapshot_prd_site(case, *size, ScrollOffset::ZERO)?; - } - } - - Ok(()) -} - -#[test] -fn sidecar_report_uses_requested_profile_id() -> Result<(), Box> { - let profile_id = ProfileId::new(); - let profile_data_dir = std::env::temp_dir().join(format!( - "ely-servo-sidecar-profile-test-{}-{}", - std::process::id(), - profile_id.as_str() - )); - let rgba_path = std::env::temp_dir().join(format!( - "ely-servo-sidecar-profile-test-{}-{}.rgba", - std::process::id(), - profile_id.as_str() - )); - - let output = Command::new(env!("CARGO_BIN_EXE_ely_servo_sidecar")) - .arg("snapshot") - .arg("--url") - .arg("data:text/html,%3Ctitle%3EProfile%20Probe%3C%2Ftitle%3EProfile%20Probe") - .arg("--profile-id") - .arg(profile_id.as_str()) - .arg("--profile-data-dir") - .arg(&profile_data_dir) - .arg("--rgba-out") - .arg(&rgba_path) - .arg("--width") - .arg("64") - .arg("--height") - .arg("64") - .output()?; - - assert!( - output.status.success(), - "stdout: {}\nstderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - let report: serde_json::Value = serde_json::from_slice(&output.stdout)?; - assert_eq!( - report.get("profile_id").and_then(serde_json::Value::as_str), - Some(profile_id.as_str()) - ); - - remove_file_if_present(rgba_path)?; - remove_dir_if_present(profile_data_dir)?; - Ok(()) -} - -fn prd_path() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..").join("..").join("PRD.md") -} - -fn remove_file_if_present(path: PathBuf) -> Result<(), Box> { - match fs::remove_file(path) { - Ok(()) => Ok(()), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()), - Err(error) => Err(error.into()), - } -} - -fn remove_dir_if_present(path: PathBuf) -> Result<(), Box> { - match fs::remove_dir_all(path) { - Ok(()) => Ok(()), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()), - Err(error) => Err(error.into()), - } -} - -fn prd_reference_urls(prd: &str) -> Vec { - prd.lines() - .filter(|line| line.starts_with("[R")) - .filter_map(|line| { - let start = line.find("https://")?; - let url = line[start..].split_whitespace().next()?; - Some(normalized_url(url)) - }) - .collect() -} - -fn normalized_url(url: &str) -> String { - url.trim().trim_end_matches('/').to_string() -} - -#[test] -fn sidecar_opens_and_renders_prd_reference_sites_to_rgba_files() -> Result<(), Box> { - for case in PRD_REFERENCE_SITE_COMPATIBILITY_CASES { - snapshot_prd_site(case, PRD_REFERENCE_SITE_SIZE, ScrollOffset::ZERO)?; - } - - Ok(()) -} - -#[test] -fn sidecar_scrolls_prd_site_with_servo_input() -> Result<(), Box> { - let scrolled_report = - snapshot_prd_site(&SERVO_SCROLL_SITE, SERVO_SCROLL_SIZE, SERVO_SCROLL_OFFSET)?; - - assert_eq!(report_field_as_i64(&scrolled_report, "scroll_x")?, SERVO_SCROLL_OFFSET.x); - assert_eq!(report_field_as_i64(&scrolled_report, "scroll_y")?, SERVO_SCROLL_OFFSET.y); - assert_eq!(report_field_as_u64(&scrolled_report, "width")?, SERVO_SCROLL_SIZE.width); - assert!(report_field_as_bool(&scrolled_report, "scroll_changed_frame")?); - - Ok(()) -} - -#[test] -fn sidecar_clicks_page_with_servo_mouse_input() -> Result<(), Box> { - let initial_report = snapshot_click_probe(None)?; - let clicked_report = snapshot_click_probe(Some(SERVO_CLICK_POINT))?; - - assert_eq!(report_field_as_u64(&clicked_report, "click_x")?, SERVO_CLICK_POINT.x); - assert_eq!(report_field_as_u64(&clicked_report, "click_y")?, SERVO_CLICK_POINT.y); - assert!(report_field_as_bool(&clicked_report, "click_changed_frame")?); - assert_ne!( - report_field_as_u64(&initial_report, "sample_hash")?, - report_field_as_u64(&clicked_report, "sample_hash")? - ); - - Ok(()) -} - -#[test] -fn sidecar_drags_page_with_servo_mouse_input() -> Result<(), Box> { - let initial_report = snapshot_drag_probe(None)?; - let drag_points = DragPoints { from: SERVO_DRAG_FROM, to: SERVO_DRAG_TO }; - let dragged_report = snapshot_drag_probe(Some(drag_points))?; - - assert_eq!(report_field_as_u64(&dragged_report, "drag_from_x")?, SERVO_DRAG_FROM.x); - assert_eq!(report_field_as_u64(&dragged_report, "drag_from_y")?, SERVO_DRAG_FROM.y); - assert_eq!(report_field_as_u64(&dragged_report, "drag_to_x")?, SERVO_DRAG_TO.x); - assert_eq!(report_field_as_u64(&dragged_report, "drag_to_y")?, SERVO_DRAG_TO.y); - assert!(report_field_as_bool(&dragged_report, "drag_changed_frame")?); - assert_ne!( - report_field_as_u64(&initial_report, "sample_hash")?, - report_field_as_u64(&dragged_report, "sample_hash")? - ); - - Ok(()) -} - -#[test] -fn sidecar_touches_page_with_servo_touch_input() -> Result<(), Box> { - let initial_report = snapshot_touch_probe(None)?; - let touched_report = snapshot_touch_probe(Some(SERVO_TOUCH_POINT))?; - - assert_eq!(report_field_as_u64(&touched_report, "touch_x")?, SERVO_TOUCH_POINT.x); - assert_eq!(report_field_as_u64(&touched_report, "touch_y")?, SERVO_TOUCH_POINT.y); - assert!(report_field_as_bool(&touched_report, "touch_changed_frame")?); - assert_ne!( - report_field_as_u64(&initial_report, "sample_hash")?, - report_field_as_u64(&touched_report, "sample_hash")? - ); - - Ok(()) -} - -#[test] -fn sidecar_types_text_with_servo_keyboard_input() -> Result<(), Box> { - let initial_report = snapshot_text_probe(None)?; - let typed_report = snapshot_text_probe(Some(SERVO_TEXT_VALUE))?; - - assert_eq!( - report_field_as_u64(&typed_report, "typed_text_byte_count")?, - SERVO_TEXT_VALUE.len() as u64 - ); - assert!(report_field_as_bool(&typed_report, "text_changed_frame")?); - assert_ne!( - report_field_as_u64(&initial_report, "sample_hash")?, - report_field_as_u64(&typed_report, "sample_hash")? - ); - - Ok(()) -} diff --git a/crates/ely_servo_host/tests/sidecar/site_cases.rs b/crates/ely_servo_host/tests/sidecar/site_cases.rs deleted file mode 100644 index a118464..0000000 --- a/crates/ely_servo_host/tests/sidecar/site_cases.rs +++ /dev/null @@ -1,141 +0,0 @@ -pub(super) const PRD_SITE_COMPATIBILITY_CASES: &[PrdSiteCompatibilityCase] = &[ - PrdSiteCompatibilityCase { url: "https://github.com", title_fragment: "GitHub" }, - PrdSiteCompatibilityCase { url: "https://example.com", title_fragment: "Example Domain" }, - PrdSiteCompatibilityCase { url: "https://servo.org/", title_fragment: "Servo" }, -]; - -pub(super) const PRD_REFERENCE_SITE_COMPATIBILITY_CASES: &[PrdSiteCompatibilityCase] = &[ - PrdSiteCompatibilityCase { - url: "https://blog.google/products-and-platforms/products/chrome/new-chrome-productivity-features/", - title_fragment: "Chrome", - }, - PrdSiteCompatibilityCase { - url: "https://www.microsoft.com/en-us/edge/features/vertical-tabs", - title_fragment: "Microsoft Edge", - }, - PrdSiteCompatibilityCase { - url: "https://resources.arc.net/hc/en-us/articles/19230755904151-Favorites-Top-Tabs-Across-Every-Space", - title_fragment: "Favorites", - }, - PrdSiteCompatibilityCase { - url: "https://resources.arc.net/hc/en-us/articles/19228855311127-Auto-Archive-Clean-as-you-go", - title_fragment: "Auto Archive", - }, - PrdSiteCompatibilityCase { - url: "https://vivaldi.com/features/workspaces/", - title_fragment: "Workspaces", - }, - PrdSiteCompatibilityCase { - url: "https://help.vivaldi.com/desktop/tabs/tab-tiling/", - title_fragment: "Tab Tiling", - }, - PrdSiteCompatibilityCase { url: "https://www.gpui.rs/", title_fragment: "gpui" }, - PrdSiteCompatibilityCase { url: "https://docs.rs/gpui", title_fragment: "gpui" }, - PrdSiteCompatibilityCase { - url: "https://zed.dev/blog/videogame", - title_fragment: "Leveraging Rust", - }, - PrdSiteCompatibilityCase { - url: "https://github.com/longbridge/gpui-component/", - title_fragment: "gpui-component", - }, - PrdSiteCompatibilityCase { - url: "https://github.com/zed-industries/awesome-gpui/", - title_fragment: "awesome-gpui", - }, - PrdSiteCompatibilityCase { url: "https://servo.org/", title_fragment: "Servo" }, - PrdSiteCompatibilityCase { - url: "https://servo.org/blog/2026/04/13/servo-0.1.0-release/", - title_fragment: "Servo", - }, - PrdSiteCompatibilityCase { - url: "https://developers.cloudflare.com/d1/", - title_fragment: "Cloudflare", - }, - PrdSiteCompatibilityCase { - url: "https://developers.cloudflare.com/workers/platform/storage-options/", - title_fragment: "Cloudflare", - }, - PrdSiteCompatibilityCase { - url: "https://developers.cloudflare.com/kv/concepts/how-kv-works/", - title_fragment: "Cloudflare", - }, - PrdSiteCompatibilityCase { - url: "https://better-auth.com/blog/1-5", - title_fragment: "Better Auth", - }, - PrdSiteCompatibilityCase { - url: "https://developers.cloudflare.com/d1/platform/limits/", - title_fragment: "Cloudflare", - }, - PrdSiteCompatibilityCase { - url: "https://component-model.bytecodealliance.org/", - title_fragment: "WebAssembly Component Model", - }, - PrdSiteCompatibilityCase { - url: "https://docs.wasmtime.dev/api/wasmtime/component/index.html", - title_fragment: "wasmtime", - }, - PrdSiteCompatibilityCase { - url: "https://docs.wasmtime.dev/security.html", - title_fragment: "Wasmtime", - }, -]; - -pub(super) const PRD_SITE_COMPATIBILITY_SIZES: &[FrameSize] = &[ - FrameSize { width: 640, height: 480 }, - FrameSize { width: 934, height: 657 }, - FrameSize { width: 1614, height: 980 }, -]; -pub(super) const PRD_REFERENCE_SITE_SIZE: FrameSize = FrameSize { width: 934, height: 657 }; -pub(super) const SERVO_SCROLL_SITE: PrdSiteCompatibilityCase = - PrdSiteCompatibilityCase { url: "https://servo.org/", title_fragment: "Servo" }; -pub(super) const SERVO_SCROLL_SIZE: FrameSize = FrameSize { width: 934, height: 657 }; -pub(super) const SERVO_SCROLL_OFFSET: ScrollOffset = ScrollOffset { x: 0, y: 480 }; -pub(super) const SERVO_CLICK_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EClick%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20onclick%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Clicked%27%3Bthis.textContent%3D%27Clicked%27%3B%22%3ETap%3C%2Fbutton%3E"; -pub(super) const SERVO_CLICK_SIZE: FrameSize = FrameSize { width: 640, height: 480 }; -pub(super) const SERVO_CLICK_POINT: ClickPoint = ClickPoint { x: 160, y: 120 }; -pub(super) const SERVO_DRAG_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3EDrag%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3B%7D%3C%2Fstyle%3E%3Cbutton%20id%3Dbox%3EDrag%3C%2Fbutton%3E%3Cscript%3Elet%20dragging%3Dfalse%3Bconst%20box%3Ddocument.getElementById%28%27box%27%29%3BaddEventListener%28%27mousedown%27%2Cevent%3D%3E%7Bif%28event.target%3D%3D%3Dbox%29%7Bdragging%3Dtrue%3B%7D%7D%29%3BaddEventListener%28%27mousemove%27%2Cevent%3D%3E%7Bif%28dragging%26%26event.clientX%3E280%29%7Bdocument.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Dragged%27%3Bbox.textContent%3D%27Dragged%27%3B%7D%7D%29%3BaddEventListener%28%27mouseup%27%2C%28%29%3D%3E%7Bdragging%3Dfalse%3B%7D%29%3B%3C%2Fscript%3E"; -pub(super) const SERVO_DRAG_SIZE: FrameSize = FrameSize { width: 640, height: 480 }; -pub(super) const SERVO_DRAG_FROM: ClickPoint = ClickPoint { x: 160, y: 120 }; -pub(super) const SERVO_DRAG_TO: ClickPoint = ClickPoint { x: 320, y: 120 }; -pub(super) const SERVO_TOUCH_URL: &str = "data:text/html,%3C%21doctype%20html%3E%3Ctitle%3ETouch%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3B%7Dbutton%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A220px%3Bheight%3A90px%3Bfont%3A28px%20sans-serif%3Bbackground%3A%23ffffff%3Bcolor%3A%23111111%3Btouch-action%3Amanipulation%3B%7D%3C%2Fstyle%3E%3Cbutton%20ontouchstart%3D%22document.body.dataset.touch%3D%27start%27%3B%22%20onclick%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.title%3D%27Touched%27%3Bthis.textContent%3D%27Touched%27%3B%22%3ETap%3C%2Fbutton%3E"; -pub(super) const SERVO_TOUCH_SIZE: FrameSize = FrameSize { width: 640, height: 480 }; -pub(super) const SERVO_TOUCH_POINT: ClickPoint = ClickPoint { x: 160, y: 120 }; -pub(super) const SERVO_TEXT_URL: &str = "data:text/html,%3C!doctype%20html%3E%3Ctitle%3EText%20Probe%3C%2Ftitle%3E%3Cstyle%3Ebody%7Bmargin%3A0%3Bbackground%3A%23f7f7f7%3Bfont%3A28px%20sans-serif%3B%7Dinput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A80px%3Bwidth%3A260px%3Bheight%3A70px%3Bfont%3A28px%20sans-serif%3B%7Doutput%7Bposition%3Aabsolute%3Bleft%3A80px%3Btop%3A180px%3Bfont%3A32px%20sans-serif%3B%7D%3C%2Fstyle%3E%3Cinput%20id%3Dq%20autofocus%20oninput%3D%22document.body.style.background%3D%27%230039ff%27%3Bdocument.getElementById%28%27out%27%29.textContent%3Dthis.value%3B%22%3E%3Coutput%20id%3Dout%3Eempty%3C%2Foutput%3E"; -pub(super) const SERVO_TEXT_SIZE: FrameSize = FrameSize { width: 640, height: 480 }; -pub(super) const SERVO_TEXT_POINT: ClickPoint = ClickPoint { x: 160, y: 120 }; -pub(super) const SERVO_TEXT_VALUE: &str = "ely42"; - -pub(super) struct PrdSiteCompatibilityCase { - pub(super) url: &'static str, - pub(super) title_fragment: &'static str, -} - -#[derive(Clone, Copy)] -pub(super) struct FrameSize { - pub(super) width: u64, - pub(super) height: u64, -} - -#[derive(Clone, Copy)] -pub(super) struct ScrollOffset { - pub(super) x: i64, - pub(super) y: i64, -} - -impl ScrollOffset { - pub(super) const ZERO: Self = Self { x: 0, y: 0 }; -} - -#[derive(Clone, Copy)] -pub(super) struct ClickPoint { - pub(super) x: u64, - pub(super) y: u64, -} - -#[derive(Clone, Copy)] -pub(super) struct DragPoints { - pub(super) from: ClickPoint, - pub(super) to: ClickPoint, -} diff --git a/crates/ely_servo_host/tests/sidecar/support.rs b/crates/ely_servo_host/tests/sidecar/support.rs deleted file mode 100644 index dc7271b..0000000 --- a/crates/ely_servo_host/tests/sidecar/support.rs +++ /dev/null @@ -1,443 +0,0 @@ -use std::{ - error::Error, - io, - path::{Path, PathBuf}, - process::{Child, Command, Output, Stdio}, - sync::Mutex, - thread, - time::{Duration, Instant}, -}; - -use ely_domain::ProfileId; - -pub(super) use super::site_cases::{ - ClickPoint, DragPoints, FrameSize, PRD_REFERENCE_SITE_COMPATIBILITY_CASES, - PRD_REFERENCE_SITE_SIZE, PRD_SITE_COMPATIBILITY_CASES, PRD_SITE_COMPATIBILITY_SIZES, - PrdSiteCompatibilityCase, SERVO_CLICK_POINT, SERVO_DRAG_FROM, SERVO_DRAG_TO, - SERVO_SCROLL_OFFSET, SERVO_SCROLL_SITE, SERVO_SCROLL_SIZE, SERVO_TEXT_VALUE, SERVO_TOUCH_POINT, - ScrollOffset, -}; -use super::site_cases::{ - SERVO_CLICK_SIZE, SERVO_CLICK_URL, SERVO_DRAG_SIZE, SERVO_DRAG_URL, SERVO_TEXT_POINT, - SERVO_TEXT_SIZE, SERVO_TEXT_URL, SERVO_TOUCH_SIZE, SERVO_TOUCH_URL, -}; - -pub(super) const MINIMUM_CONTENT_PIXELS: u64 = 1_000; -const SIDECAR_TIMEOUT: Duration = Duration::from_secs(45); -const SIDECAR_POLL_INTERVAL: Duration = Duration::from_millis(20); -const SIDECAR_COMMAND_COOLDOWN: Duration = Duration::from_millis(750); -const SIDECAR_RETRY_INTERVAL: Duration = Duration::from_millis(250); -const SIDECAR_MAX_ATTEMPTS: usize = 3; -static SIDECAR_COMMAND_LOCK: Mutex<()> = Mutex::new(()); - -#[derive(Clone, Copy, Default)] -struct SnapshotInput<'a> { - click_point: Option, - drag_points: Option, - touch_point: Option, - typed_text: Option<&'a str>, -} - -pub(super) fn snapshot_prd_site( - case: &PrdSiteCompatibilityCase, - size: FrameSize, - scroll_offset: ScrollOffset, -) -> Result> { - let site_name = case - .url - .chars() - .map(|character| if character.is_ascii_alphanumeric() { character } else { '-' }) - .collect::(); - let output_path = std::env::temp_dir().join(format!( - "ely-servo-sidecar-{}-{site_name}-{}x{}-{}-{}.rgba", - std::process::id(), - size.width, - size.height, - scroll_offset.x, - scroll_offset.y - )); - - snapshot_prd_site_with_retry(case, &output_path, size, scroll_offset) -} - -fn snapshot_prd_site_with_retry( - case: &PrdSiteCompatibilityCase, - output_path: &Path, - size: FrameSize, - scroll_offset: ScrollOffset, -) -> Result> { - for attempt in 0..SIDECAR_MAX_ATTEMPTS { - match snapshot_prd_site_once(case, output_path, size, scroll_offset) { - Ok(report) => return Ok(report), - Err(error) if attempt + 1 == SIDECAR_MAX_ATTEMPTS => return Err(error), - Err(_) => remove_file_if_present(output_path)?, - } - - thread::sleep(SIDECAR_RETRY_INTERVAL); - } - - Err("sidecar PRD snapshot retry did not produce output".into()) -} - -fn snapshot_prd_site_once( - case: &PrdSiteCompatibilityCase, - output_path: &Path, - size: FrameSize, - scroll_offset: ScrollOffset, -) -> Result> { - let output = run_sidecar_snapshot_with_retry( - case.url, - output_path, - size, - scroll_offset, - SnapshotInput::default(), - )?; - - assert!( - output.status.success(), - "{} {}x{}\nstatus: {:?}\nstdout: {}\nstderr: {}", - case.url, - size.width, - size.height, - output.status.code(), - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - - let report: serde_json::Value = serde_json::from_slice(&output.stdout)?; - assert_report_state_is_renderable(&report)?; - assert_eq!(report_field_as_u64(&report, "width")?, size.width, "{}", case.url); - assert_eq!(report_field_as_u64(&report, "height")?, size.height, "{}", case.url); - assert_eq!( - report_field_as_u64(&report, "rgba_byte_count")?, - size.width * size.height * 4, - "{}", - case.url - ); - assert_report_text_contains(&report, "loaded_url", case.url)?; - assert_report_text_equals(&report, "requested_url", case.url)?; - assert_report_text_contains(&report, "title", case.title_fragment)?; - assert!(report_field_as_u64(&report, "non_white_pixel_count")? > 0, "{}", case.url); - assert!( - report_field_as_u64(&report, "content_pixel_count")? >= MINIMUM_CONTENT_PIXELS, - "{}", - case.url - ); - assert!(report_field_as_u64(&report, "sample_hash")? > 0, "{}", case.url); - assert_eq!(std::fs::metadata(output_path)?.len(), size.width * size.height * 4); - - log_prd_report(&report, case, size)?; - - std::fs::remove_file(output_path)?; - Ok(report) -} - -fn remove_file_if_present(path: &Path) -> Result<(), Box> { - match std::fs::remove_file(path) { - Ok(()) => Ok(()), - Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()), - Err(error) => Err(error.into()), - } -} - -pub(super) fn snapshot_click_probe( - click_point: Option, -) -> Result> { - snapshot_probe( - SERVO_CLICK_URL, - SERVO_CLICK_SIZE, - "click", - SnapshotInput { click_point, ..SnapshotInput::default() }, - ) -} - -pub(super) fn snapshot_drag_probe( - drag_points: Option, -) -> Result> { - snapshot_probe( - SERVO_DRAG_URL, - SERVO_DRAG_SIZE, - "drag", - SnapshotInput { drag_points, ..SnapshotInput::default() }, - ) -} - -pub(super) fn snapshot_touch_probe( - touch_point: Option, -) -> Result> { - snapshot_probe( - SERVO_TOUCH_URL, - SERVO_TOUCH_SIZE, - "touch", - SnapshotInput { touch_point, ..SnapshotInput::default() }, - ) -} - -pub(super) fn snapshot_text_probe( - typed_text: Option<&str>, -) -> Result> { - snapshot_probe( - SERVO_TEXT_URL, - SERVO_TEXT_SIZE, - "text", - SnapshotInput { - click_point: typed_text.map(|_| SERVO_TEXT_POINT), - typed_text, - ..SnapshotInput::default() - }, - ) -} - -fn snapshot_probe( - url: &str, - size: FrameSize, - label: &'static str, - input: SnapshotInput<'_>, -) -> Result> { - let output_path = std::env::temp_dir().join(format!( - "ely-servo-sidecar-{}-{label}-{}x{}.rgba", - std::process::id(), - size.width, - size.height - )); - - if output_path.exists() { - std::fs::remove_file(&output_path)?; - } - - let output = - run_sidecar_snapshot_with_retry(url, &output_path, size, ScrollOffset::ZERO, input)?; - - assert!( - output.status.success(), - "{label} probe\nstatus: {:?}\nstdout: {}\nstderr: {}", - output.status.code(), - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - - let report: serde_json::Value = serde_json::from_slice(&output.stdout)?; - assert_eq!(report_field_as_u64(&report, "width")?, size.width); - assert_eq!(report_field_as_u64(&report, "height")?, size.height); - assert!(report_field_as_u64(&report, "content_pixel_count")? > 0); - assert_eq!(std::fs::metadata(&output_path)?.len(), size.width * size.height * 4); - - std::fs::remove_file(&output_path)?; - Ok(report) -} - -fn run_sidecar_snapshot( - site_url: &str, - output_path: &Path, - size: FrameSize, - scroll_offset: ScrollOffset, - input: SnapshotInput<'_>, -) -> Result> { - let _guard = SIDECAR_COMMAND_LOCK - .lock() - .map_err(|_| io::Error::other("sidecar command lock poisoned"))?; - let profile_id = ProfileId::new(); - let profile_data_dir = temporary_profile_data_dir(&profile_id); - let mut command = Command::new(env!("CARGO_BIN_EXE_ely_servo_sidecar")); - command - .arg("snapshot") - .arg("--url") - .arg(site_url) - .arg("--profile-id") - .arg(profile_id.as_str()) - .arg("--profile-data-dir") - .arg(&profile_data_dir) - .arg("--rgba-out") - .arg(output_path) - .arg("--width") - .arg(size.width.to_string()) - .arg("--height") - .arg(size.height.to_string()); - if scroll_offset.x != 0 { - command.arg("--scroll-x").arg(scroll_offset.x.to_string()); - } - if scroll_offset.y != 0 { - command.arg("--scroll-y").arg(scroll_offset.y.to_string()); - } - if let Some(click_point) = input.click_point { - command.arg("--click-x").arg(click_point.x.to_string()); - command.arg("--click-y").arg(click_point.y.to_string()); - } - if let Some(drag_points) = input.drag_points { - command.arg("--drag-from-x").arg(drag_points.from.x.to_string()); - command.arg("--drag-from-y").arg(drag_points.from.y.to_string()); - command.arg("--drag-to-x").arg(drag_points.to.x.to_string()); - command.arg("--drag-to-y").arg(drag_points.to.y.to_string()); - } - if let Some(touch_point) = input.touch_point { - command.arg("--touch-x").arg(touch_point.x.to_string()); - command.arg("--touch-y").arg(touch_point.y.to_string()); - } - if let Some(typed_text) = input.typed_text { - command.arg("--type-text").arg(typed_text); - } - - let mut child = command.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?; - let started_at = Instant::now(); - loop { - if child.try_wait()?.is_some() { - let output = child.wait_with_output()?; - remove_temporary_dir(&profile_data_dir)?; - thread::sleep(SIDECAR_COMMAND_COOLDOWN); - return Ok(output); - } - - if started_at.elapsed() >= SIDECAR_TIMEOUT { - terminate_child(child)?; - remove_temporary_dir(&profile_data_dir)?; - thread::sleep(SIDECAR_COMMAND_COOLDOWN); - return Err(format!( - "timed out rendering {site_url} at {}x{}", - size.width, size.height - ) - .into()); - } - - thread::sleep(SIDECAR_POLL_INTERVAL); - } -} - -fn temporary_profile_data_dir(profile_id: &ProfileId) -> PathBuf { - std::env::temp_dir().join(format!( - "ely-servo-sidecar-profile-{}-{}", - std::process::id(), - profile_id.as_str() - )) -} - -fn remove_temporary_dir(path: &Path) -> Result<(), Box> { - match std::fs::remove_dir_all(path) { - Ok(()) => Ok(()), - Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()), - Err(error) => Err(error.into()), - } -} - -fn run_sidecar_snapshot_with_retry( - site_url: &str, - output_path: &std::path::Path, - size: FrameSize, - scroll_offset: ScrollOffset, - input: SnapshotInput<'_>, -) -> Result> { - for attempt in 0..SIDECAR_MAX_ATTEMPTS { - if output_path.exists() { - std::fs::remove_file(output_path)?; - } - - match run_sidecar_snapshot(site_url, output_path, size, scroll_offset, input) { - Ok(output) if output.status.success() => return Ok(output), - Ok(output) if attempt + 1 == SIDECAR_MAX_ATTEMPTS => return Ok(output), - Ok(_output) => {} - Err(error) if attempt + 1 == SIDECAR_MAX_ATTEMPTS => return Err(error), - Err(_error) => {} - } - - thread::sleep(SIDECAR_RETRY_INTERVAL); - } - - Err("sidecar snapshot retry did not produce output".into()) -} - -fn terminate_child(mut child: Child) -> Result<(), Box> { - match child.kill() { - Ok(()) => { - let _output = child.wait_with_output()?; - Ok(()) - } - Err(error) if error.kind() == io::ErrorKind::InvalidInput => Ok(()), - Err(error) => Err(error.into()), - } -} - -fn assert_report_text_equals( - report: &serde_json::Value, - field: &'static str, - expected: &str, -) -> Result<(), Box> { - let value = report_field_as_text(report, field)?; - if value == expected { Ok(()) } else { Err(format!("{field}: {value}").into()) } -} - -fn assert_report_text_contains( - report: &serde_json::Value, - field: &'static str, - fragment: &str, -) -> Result<(), Box> { - let value = report_field_as_text(report, field)?; - if value.contains(fragment) { Ok(()) } else { Err(format!("{field}: {value}").into()) } -} - -fn assert_report_state_is_renderable(report: &serde_json::Value) -> Result<(), Box> { - let state = report_field_as_text(report, "state")?; - if matches!(state, "complete" | "loading") { - Ok(()) - } else { - Err(format!("state: {state}").into()) - } -} - -fn log_prd_report( - report: &serde_json::Value, - case: &PrdSiteCompatibilityCase, - size: FrameSize, -) -> Result<(), Box> { - eprintln!( - "prd-live-site servo-sidecar url={} loaded={} title={} state={} size={}x{} content_pixels={} non_white_pixels={} sample_hash={}", - case.url, - report_field_as_text(report, "loaded_url")?, - report_field_as_text(report, "title")?, - report_field_as_text(report, "state")?, - size.width, - size.height, - report_field_as_u64(report, "content_pixel_count")?, - report_field_as_u64(report, "non_white_pixel_count")?, - report_field_as_u64(report, "sample_hash")? - ); - Ok(()) -} - -fn report_field_as_text<'a>( - report: &'a serde_json::Value, - field: &'static str, -) -> Result<&'a str, Box> { - report - .get(field) - .and_then(serde_json::Value::as_str) - .ok_or_else(|| format!("missing text report field: {field}").into()) -} - -pub(super) fn report_field_as_bool( - report: &serde_json::Value, - field: &'static str, -) -> Result> { - report - .get(field) - .and_then(serde_json::Value::as_bool) - .ok_or_else(|| format!("missing boolean report field: {field}").into()) -} - -pub(super) fn report_field_as_i64( - report: &serde_json::Value, - field: &'static str, -) -> Result> { - report - .get(field) - .and_then(serde_json::Value::as_i64) - .ok_or_else(|| format!("missing signed report field: {field}").into()) -} - -pub(super) fn report_field_as_u64( - report: &serde_json::Value, - field: &'static str, -) -> Result> { - report - .get(field) - .and_then(serde_json::Value::as_u64) - .ok_or_else(|| format!("missing numeric report field: {field}").into()) -} diff --git a/crates/ely_servo_host/tests/software_host.rs b/crates/ely_servo_host/tests/software_host.rs index 0e61b18..bbf0790 100644 --- a/crates/ely_servo_host/tests/software_host.rs +++ b/crates/ely_servo_host/tests/software_host.rs @@ -11,9 +11,8 @@ use std::{ use ely_domain::{ProfileId, SiteOrigin, SitePermissionFeature, TabId, UrlText}; use ely_servo_host::{ HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseDragRequest, NavigationRequest, - PageZoomRequest, PermissionDecision, PermissionRequest, ResizeRequest, ScreenshotRequest, - ScrollRequest, ServoHost, ServoHostError, ServoSurfaceSize, SoftwareServoHost, TouchTapRequest, - WebViewState, + PageZoomRequest, PermissionDecision, PermissionRequest, ResizeRequest, ScrollRequest, + ServoHost, ServoHostError, ServoSurfaceSize, SoftwareServoHost, TouchTapRequest, WebViewState, }; const MINIMUM_CONTENT_PIXELS: u64 = 1_000; @@ -336,17 +335,6 @@ fn exercise_real_servo_webview_lifecycle() -> Result<(), Box> { site.url ); assert_rendered_frame_has_content(&host, site.url, MINIMUM_CONTENT_PIXELS)?; - if site.url == "https://example.com" { - let screenshot = - host.capture_screenshot(ScreenshotRequest { webview_id: webview_id.clone() })?; - assert_frame_has_dimensions_and_content( - &screenshot, - "https://example.com screenshot", - INITIAL_WIDTH, - INITIAL_HEIGHT, - MINIMUM_CONTENT_PIXELS, - ); - } previous_frame_hash = Some(host.last_rendered_frame()?.sample_hash()); } diff --git a/docs/servo-embedding-architecture.md b/docs/servo-embedding-architecture.md index bd4d4e0..306324e 100644 --- a/docs/servo-embedding-architecture.md +++ b/docs/servo-embedding-architecture.md @@ -2,46 +2,52 @@ ## Decision -ELY is a Servo-based browser. The page renderer is Servo itself, embedded in the application process and attached to a real platform rendering surface. The browser chrome can stay GPUI, while web content must follow Servo's embedder model: +ELY is a Servo-based browser. The page renderer lives in the application process and follows Servo's embedder model: ```text ┌──────────────────────────── ELY App Process ────────────────────────────┐ -│ │ -│ GPUI chrome │ -│ ┌───────────────────────────────────────────────────────────────────┐ │ -│ │ Sidebar Toolbar Tabs Settings │ │ -│ └───────────────────────────────────────────────────────────────────┘ │ -│ │ -│ Servo content host │ -│ ┌───────────────────────────────────────────────────────────────────┐ │ -│ │ Servo + WebView + WindowRenderingContext │ │ -│ │ notify_new_frame_ready -> window repaint -> paint -> present │ │ -│ └───────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────┘ +│ │ +│ GPUI chrome │ +│ ┌────────────────────────────────────────────────────────────────────┐ │ +│ │ Sidebar Toolbar Tabs Settings │ │ +│ └────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ Servo content host │ +│ ┌────────────────────────────────────────────────────────────────────┐ │ +│ │ Servo + WebView + RenderingContext │ │ +│ │ notify_new_frame_ready -> repaint -> paint -> present │ │ +│ └────────────────────────────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────┘ ``` -The normal page-display path excludes external rendering sidecars, stdout frame transport, RGBA frame payloads, cross-process IOSurface handoff, and GPUI `RenderImage` uploads for live web content. +The app process owns WebView lifecycle, navigation, input, permissions, frame readiness, and rendering context presentation. -## Root Cause - -The current ELY page path is a remote-frame architecture: +## Current Code Boundary ```text -GPUI shell - -> WebSurfaceStore +WebSurfaceStore + -> GPUI NativeSurface -> LiveRuntimeWorker -> ServoLiveClient - -> ely_servo_sidecar stdin/stdout JSON -> SoftwareServoHost - -> Servo WebView paint - -> RGBA payload or IOSurface handle - -> GPUI surface/image element + -> servo::Servo + servo::WebView + servo::WindowRenderingContext ``` -This makes page interaction depend on worker scheduling, IPC, polling cadence, surface import, frame object churn, and GPUI scene refresh. Hardware IOSurface reduces byte volume, while the architecture still behaves like a remote compositor. +`ServoLiveClient` is now an in-process adapter over `ely_servo_host::SoftwareServoHost`. It shares one Servo runtime across profile scopes, creates multiple WebViews inside that runtime, and routes scroll, hover, click, keyboard, resize, zoom, navigation, and permissions through the `ServoHost` API. -Servo's own embedder route is direct: +Normal page display now enters GPUI through `native_surface(...)`. GPUI creates a platform child surface for the content bounds, passes that raw handle into Servo's `WindowRenderingContext`, and presents with `paint_without_readback_with_completion`. The RGBA readback path remains inside `ely_servo_host` for low-level tests. + +Current platform child surfaces: + +- macOS: child `NSView` with an AppKit raw window handle. +- Windows: child `HWND` with a Win32 raw window handle. +- Linux/X11: child XCB window with XCB display/window handles. +- Linux/Wayland: child `wl_surface` attached as a `wl_subsurface` with Wayland display/surface handles. + +## Upstream Servo Route + +Servo's own shell route is the model for the final ELY rendering path: ```text Window event @@ -57,7 +63,7 @@ Relevant upstream evidence from Servo `7c48af7`: - `ports/servoshell/window.rs` creates `WebViewBuilder::new(state.servo(), platform_window.rendering_context())`. - `ports/servoshell/window.rs` repaints with `webview.paint()` and `rendering_context().present()`. - `ports/servoshell/running_app_state.rs` handles `notify_new_frame_ready` by marking the owning window for repaint. -- `components/paint/paint.rs` owns one WebRender painter per `RenderingContext` and explicitly avoids blocking paint on the constellation. +- `components/paint/paint.rs` owns one WebRender painter per `RenderingContext` and keeps paint coordination inside Servo's rendering pipeline. ## Target Boundaries @@ -73,11 +79,11 @@ crates/ely_app/src/servo_embed WebView lifecycle, repaint dispatch, and web input routing. crates/ely_servo_host - Transitional compatibility surface for explicit screenshots and isolated - compatibility tools. Normal live page display leaves this crate. + Owns the current in-process compatibility adapter while the native + platform rendering surface lands. ``` -Each production source file in the new embedding path stays below 500 lines. Large responsibilities split by ownership: +Each production source file in the final embedding path stays below 500 lines. Large responsibilities split by ownership: - `runtime.rs`: `Servo`, wake handling, webview registry. - `platform_view.rs`: platform content surface attachment. @@ -88,36 +94,33 @@ Each production source file in the new embedding path stays below 500 lines. Lar ## Migration Slices -1. Create `servo_embed` as an in-process module behind the existing tab/domain model. -2. Add a macOS platform content view using the GPUI window's raw AppKit handle. -3. Build Servo `WindowRenderingContext` or child context against the platform content view. -4. Move one active tab to in-process `Servo + WebView + RenderingContext`. -5. Route scroll, mouse, keyboard, resize, zoom, and navigation directly into the active `WebView`. -6. Replace `WebSurfaceStore` for normal web pages with the in-process host. -7. Delete sidecar spawning from normal page display. -8. Keep explicit page screenshot capture as a user-command path only. +1. App process owns the Servo runtime and WebView registry. +2. App process routes web input and navigation directly into Servo. +3. GPUI exposes one `NativeSurfaceHandle` element contract for platform child surfaces. +4. macOS creates a child `NSView` and hands the AppKit raw handle to Servo. +5. Windows creates a child `HWND` and hands the Win32 raw handle to Servo. +6. Linux/X11 creates a child XCB window and hands the XCB raw handle to Servo. +7. Linux/Wayland creates a child `wl_surface`/subsurface and hands the Wayland raw handle to Servo. +8. GPUI web page display uses native content surface presentation for every platform target. ## Acceptance Gates -- `cargo run` opens a normal web page without starting `ely_servo_sidecar`. +- `cargo run` opens a normal web page with Servo inside the ELY app process. - Scrolling a live web page uses Servo input events and Servo repaint callbacks. -- The page display path contains no RGBA frame payload transport. -- The page display path contains no stdout JSON frame loop. -- The page display path contains no GPUI `RenderImage` upload for live web content. -- Address/search, tabs, spaces, profiles, settings, permissions, sync, and explicit screenshots continue to compile and behave through existing domain APIs. +- Page display presents through a Servo rendering context. +- macOS, Windows, Linux/X11, and Linux/Wayland each have a platform child surface implementation under the same `NativeSurfaceHandle` contract. +- Address/search, tabs, spaces, profiles, settings, permissions, and sync compile through existing domain APIs. - Every new source file stays below 500 lines. -- No user-facing frontend status, logs, debug panels, or explanatory clutter are added. +- User-facing chrome stays clean. -## First Implementation Target - -The first code slice is macOS content-view attachment: +## First Platform Surface Target ```text GPUI Window - -> raw AppKit NSView - -> ELY child NSView for web content bounds + -> GPUI NativeSurface element + -> platform child surface for web content bounds -> Servo WindowRenderingContext -> Servo WebView ``` -This slice creates the real platform surface required by Servo's direct rendering model. Once the content view exists, Servo can paint into a native surface in the ELY app process, and the remote-frame path can be removed tab by tab. +This slice gives Servo a native surface in the ELY app process. The compatibility adapter remains a narrow bridge for tests and non-normal display probes. diff --git a/generated-icons/android/ic_launcher_hdpi.png b/generated-icons/android/ic_launcher_hdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..60283bac03c69744eecb6cf27fd1a6e541528982 GIT binary patch literal 4228 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 TN*9dI00000NkvXXu0mjfcwU~8 literal 0 HcmV?d00001 diff --git a/generated-icons/android/ic_launcher_xhdpi.png b/generated-icons/android/ic_launcher_xhdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..0567ceb1db4c47573c867b71870603c4b6736df3 GIT binary patch literal 6216 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

NT~K#2(Q-o~*d~v~)eS1c*>r8f?)`V? z*z5oNX0;OqPrKI~;|8u_BXTiZEFGvP>?Jya95yZ+0)dr=u5O1xq{wOx=B4VQ0--(l z1H-9flibHDP1t;E#&&+Z+kWfI3L6DqkOL|mR*p}K%64=?2kknlSN4V<1Pcfg&TSfDh%!6Xd%7hMS)JRe#y24aKk=l zCVT)x?k%iOPFRwHVK0(u+h`nNBMf?M)Rv3F$LS6T5Prj*C=flEKG+HNF}*8)=zSC+ z4lT?8Q9;s5=n)R&o|(A%c#&u}we2+_6fKo+c);==sR817k za)Nuop{79Szn@r@A zDm6E2hng&h9%)lAQ?;WNDwajH)OtRfn%kO_AyCjV8Y2$BUZnQ%Czq3_QEdS{Q88b1 z$Q$wLd$WnLlhDU05a5%13i>!Tshjd;!FIQoY(Cfa2u1Ea#qfx%7$xNQE99oTSvIYA zpHAyN{;}^6ggfrY{D5G02L&>{PVMf{F^unx(2O;+sbkZGIw2%`eBm9kXqFe0EyMN6n9lIJ*;w^Z}xxLat1z zeBkZEAX!@;Y<8>XncBWSfWn|0;l3<{Mvxj#)g$x@{#C=!^bHh9T?j&V5Kf`xF>K%FSujd5x`xXeU~Vw4I_V(^dbJjMv_hc9z()}RSl=v%`vwOK zhwglGE{1an2L?yTdSX0hpK^BfejpR+a6L)8AKb~Pot~~H^IRs+F&w{E zcRR_aL|K4=PzXBFJ~Fkm+nK|%DQ#b+=}s{b0)@~=uCnQ^Cp|)7YjXKD$?m;emzGDd zKd=;dIa3f$;n1i)K=*V)EP#)~G_Z;Gp;f`&4_7!JE2Bb`J&}(ws`g%Sw-6ZP_X&aH zoJz!*yaS}Ca429goT+trZ;LP*1Ohk+)A$v>4un6}5{RJ^;L-RLZtq4$XlRfSBori_ z!XlKowipgV7%>P2xrr)*h5o|e4<|Ift@)s!=z#zX=D_^7*)-4CbD`t?bQu%=Xx6UT5^<2# z(yXU93Pf>dE5Xft0Z|;S9u0-GazZVL^^`tp0Xd)#K_fmuB;zAH$qm)0c>S&yR|LpX zXc{~+@(dqf*k0+a5O8y!9t@&{KqRaEkO?w&3>Z-KyopEgm2>K)_%ImMFsYVET{`6_ z#8CT%MHB*=5g;pHgGV+jGOsC?D&eWV4IrxB+LG!M0X4pM!4f}2GOd8sKo7#VRUes- zkdgaRyEB}h@%>EiV)i~V;EMToZNdmp?b&ii^rvl3n+ND6qZQ+qtSlwxH!s9!4lPH* zxjbNFUA0`pRUYPH`NkNkmvx$Z1y`1eJCb+Dp9jDhywORhCBUtoiMr1xus_Xf8l8ybk6tW_JsQEdW$IRDNBicK+pc%eTqo zOZDoNPOiUNc|wK2;RA3Y-7XLFdc#7>8Px>ChCq01U)hE3!P?HH`rBSh;8;~Z_ujwB zw$80J%mhaTM@zI7)+JgvCi;^vOSb*yE?X{EEK4)=OExEw3?Cqf@q0j^aI9@t#_>9i z_bFeXK4~(FsLSfgvgF2KpI1-;Eu2PHG2vIwGJ_u z59r#nt=`>D4f9u4Qk-b!Cwu_h5>SjE@@}nKa}&c~MtLOiVb1#M6HxWMfQqm{LBPVG zcem;{NuRsG=%hu1ta$)8^Yd_r*dZ7`L4d*nKXA<)v%W^02P}W)*aNPLx#A~xADsw8 z*J~bt#Q!+s$1uG`L}Z7m#{-1Hgh0*mvL;b!{gMxsq^=Yj^^`%8k3znQsCMkRU^?kOnp0T&S zZT*lK<8V1}`{pjY{>C<&OeW1VBWEYw4=~pPAQV+|(dLc|{ly54n zmO0zsUb1W=w^~oD-K@E`mE%bzE9WSp|flj9y z^VfT?pLimCfNOw7K7o4vF*$v9^5W1k0_?<#EB%wWJQ8 zYSY6tLw1Z8&g6u!wE24bW*K#2YjoQpkpdupNxg}$`o=)DlePAKz)2?gZJ%i7cf%sH z1D!GT$cu{YDx%s$d#66nsQVzqJ+S9fSV9z^U$pZfFPI? zUYr(!Cb9n0h(V$ROyQ(1Y+5^wb;m9N=+2S4C}bi8%PIrY;;lekrc+{^cm^kl@Oc28 z&mhl%$uOUD4D<8^%-N;B3s%FR=HY2jM7IdKHRuY^LZEYjnsun5L6fGK1~RtG3TR9n zYX#gIAEqG!fRt_XPPrYW+6^+aee<_H*4#iJ^EYex^|ZDfxc(m*Oxp+ec5ml~sxsYs z0$tyYVO@D0-2@xlxuN^peVIj-Qx?b?2XZ}jS$#zI z<-1rNUb3TGD^Hxlpe_(*!=NzcugbyQ6_5x}n~qrn_*R+vYiWMldemj{$vbt9LNiO) zHDQMBW7-)CIAJ|vh2CX-aEo+GLnvwQBjxlW4_1dF25AgxF)b7v?UhOMrvL6uJW zOYxZIb1=W2r)y&!=5)-jUUWySgEkNn?r08gn+^rf=-*tv4G)#|fn-A=yB?NLHZmd{ z@>LER+W^!g?qaxQOom$!L_2QF0A@D`?h2v#2YQ>9umC2u1fxpmzU2jBku_#v3rt%t z5DHmV4&gEp34`c+Wo4bxVb23#xL-fk%h*;yFK6zyj6oujumUi?uSb3$%rRbg>EMuR zI{w&>&X_2o&Ue8eBjSU$(Itv%4g$YRs*X{21V|%JI4bI#U`)vwn$GkMFvz1bzhNaP zD?rQ*pk=_gj1PRPJ|P@3EQoo`0)!sDC*YX6XNt6`v^dosTsD$@PtA)Bfk;MZk2**J zpSV$m6b!(igg=16p|(px2wd=?zAg3+c`qe9LEXajh zAp~Lo>ttp7w6;&B6UP_~a%p|1A|Nt-%Hvea`$@hdA$dO$jNeMqYW;hA-%yQ@LIG3r z8rwzX zXSd{f$2aP?p^!CV7-Im3mx4?!^Lu88OH}*Yf&dqnVaLWeVZOcvB6VXx1BR6gxv|Kx zRc`5|C*gNOaH)c+wa=fQ0-;0!!E{@)eNp=`UEIObu&O zn`NO^fCzJv*dNpWdi$m(r!~Hr=7I_k20?HlEQ^#dd99LOz>-q{Z*Opq*@oGKWqagU=RQZ0-Oal;7JQ%+|i(x0iZ=7 zgwA@Np_T#T3uLxB-1!S*T5ogSK?b&SN+fa4lfC`jGMP-TCHeZ~+htk4mQ6Av5~LHw zU?%jj-?}obfW3|f{b|u3%F$NsQNnN$M+hON z(-{+`cx^SAd^`CMKf`bFgkP2W;Yd_e7gD9QiVo9kY3YCvC29pw*kgxU4WUDvYE($U zc=7A-ghNF%@;RmBN9BArk=w&uz3)DLtBQXn3BOWBg7B5Zh#(k%6{%orehUt{mO>b? zsS_Z*A6ppgJbOU(UrWR3d&f1E*L%Ky)Z3e`z$dgI{IGgWEhm+ zewbZdt%`q|%_nkGHRFq+E=}tz(@ul|=5HfGdNim#2Y6qDUlufLU=Rg$^s}d77|i=5rqR$z0^Fk<6$wKYYW> zv+SCBI)fh?0Yxl0q#e11%%O!5m=!~W?m4A6D%a*2OL+DS=RB9zAh~p@X80tTesOoV zC>Z1EI5(Nn%vuzN@(aWOh6sX84rkXfA$AaSJmJ(`N}NmhQ)JWRi%m+IaL&umepV!p zKEki%dGYt_8x#3N3j3ir7@Bg34LC$Bg8)82+onc_e-ObhWVuLKbQt_OM8luTkDr7P z#hHzDmgo83pLy%-n)s}!c)xOjFI}pg;e?CN%MFqb?v9%AdAM>@XK-i@#GS$gAs+TA z9SJ3=obKhHm&aEwU1q1;Zg%iNj4reZ#yC5y%K#q&S4@d#sVC0B$RyFy&K!qWG?moL+V z|Nd;&u9X;>K$|*4f?vxJp*Q@vJQ#xmk8m8P)KXcNY(AUtEKPo&bG~F(SR3wd1>n*p zR!Co9W14FLM67lDCBrsdRqPjj=fA#pTN) z{m}a+U*6qWeP#2^G?m^n(r!}$PtuvLEy8K--(h?W&wB)76w3gZrG$4TALw`ZkrEttZJzy+3z(;u@5W_2xG>+i2F!`} zePE+K7tVbB&UI=1onX$w=uUBhC7j8!Or#0loh0*z=Wk`-uXTK<{Uur732X`^?|V1@ zmm(Lxy}6mmw}(+HfWr&bPRD><+khdepJXa6x&zCChiLTKJai}N3`=JRr-v6fY&wm( z0xGt)){<3T{`T72_`g(9e!%>a&<|xwGx6MW&t*UR@26k5eY^PUt+Ugt6e4f)t>I0i#VjCSpa6FzoatLe3^@+^)pC2=RRD?fHZk5ehi{GM}XvYR(| z{`%f`Yu`6uX@h!5z6d^~SB1k%bM5Cr0P zfB=+bS#E5sCq+?ww_NRi@a+FQdy_pSw_U{-xRaksmK7d)i1D9$m|rhN`HM^x+mlHu zKQh#s5mN)>Rsd>$SO)&|IvA$b{#Ym2L&mHX1&3T(-?~4Ze7p=DeHzr!DYdITClk4% zx!lf+{1?x?154d5k;O(X+r>S#@3d{ek7XGT|0LS0;Ch1rVYX zX_}R@*(6!4mXDvmu=y3myRLeq1u*>qk^QGfCtq1E^G~j=XK9x5Qg&G7w*?VR4v$+V z=vMu-n!XdCreC~m$VhrW&@xyS0eR3+HPFv^7-}cmg-kkz={aFkic%y=Dxpuhw^)9% zVSW<#M+tzW#~$N(B?KRw{rUdh>N8RZ(j*a5>wRu!Q0 zYcM@F$%P3@AQM`!%u=)c_|?Gw5p5vd`wQL$uqRDYvAMaP?(A)U=DwfW{PTwS+n~*0 z0nmlu=N_GYes_2I$>}WPSypW-S{SBQ7Xob3iv|8b`Hs0YN3~72O~0PcT={Jpt>G<3 z_mE!3G?RF_gXYJ(h~Ev8leCj8OXjmF-`&~$qv$MC#6UMW4X|o>rY0kAjosK5(2h?Q7N=JZ~%?J>P{OA}Oi{KcRCT;?Z^@sqW zxIQ8RWYsm&z?=(lKlL_PtcBsnE7ZBMwvjPb-d^OZUpaqaYvOmLA`u~UNqHOCM#E(V7PE8t>&bwc?LDy=2RP~iHUp+oXGWc^U{wM!Qh zc^HhxR2i9BIX^x#yh4cl{JAa3{4Xx|mzOH$kJS9V1)vjx=duq!I{P}?OFpz(luw`A znpEB~6J_OibWE>pd>F{^T|TJS^l$hHjexZ2dYSIFxfxy4{?$1=c4nH#E+!4*Ax)Tq zneI-UR_nL6Crwj+{+=`0{$lm?YPtKNd)~SE^=hK)v3TZ}G)S)H!nu!sy!N#J_t*1J z3C{o9WRjfSzP&2y#h}fXXULbrVfTmw3pFyt{KVuGbG-aw9wSbB!1^8tf2cC5P1K47 zz#siC(U6FSKX=0=U&ziz#K`rjcB_MyR-)uJBG%m4dz@zVt> z{~((rPvkjc`+F-nuu2fBvTdcXQMs_M&M*{X)#*024~^|p0;0DSH3LlM(E{MQ2fgq= zlE~Td6adNi67OuRZ=_k4vQ@EqCgbJr%-=Hqj#{+on&dh#2&9u7fMkAYhW_IR_;-Hx z1L>2iJpTnQ%5QF+ouu>mq>2>D7ZXUR69SDasMNtDsFk60sK;p|7Wc$fR}UVW?SQ>i zP#?n<(lLl^%hkydYVw%qj{L37*OK%1Y^9|rzq!a4zp(jlrcX-dZ`SsdLz#c{+acd5 zd+Mo5D9W#WO-vRU`&ChvzcraA7o}&gyPHc_P-3-2ewdHDW`rqc6=+E^q5_y7EAJD4 zA<-YzFC_q2S=AmKvtEAeH9*J30yHp3ey}k+v$@ISM~jxr#kDle{&4l+zgEErqtujHf7B@c*tsjAN7_8`F|m&G6! zGr5;0=**A75*($#gnp^VyWijcT`dmh#!Zkfh0%r?cth***LlT7CHf`zUY+ zqXRZ<^%Ky{? zRF6|gK^hhfeH<2|6%x;+mJ9xn+I@t#z>u==ei>=(-~^!g6e*J*q)XFG?n2H)S+c$T zy^=HbYMQ23lO+8+p0Tg4-#7V&QkG3%B{@jOKRf}@NMg7vS6CwF5%l!0eNUVfd+fng zUR-2CTr3LyfDqyWW8!|s*p?7tUA~iP3>sx{pXPT6iNWMOw7&=bkb+`d%iV+jMYTpJ z*=3yFl3!zF34bL`_;(YYT$@g&*Cq*jk)P)`K^?hCgJk;AFusreKa@LO`7@=dM*si- M07*qoM6N<$f`C$q82|tP literal 0 HcmV?d00001 diff --git a/generated-icons/android/playstore-512.png b/generated-icons/android/playstore-512.png new file mode 100644 index 0000000000000000000000000000000000000000..7f4b8110f8f2b83be0d292718bd1c33891484967 GIT binary patch literal 49433 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 literal 0 HcmV?d00001 diff --git a/generated-icons/icon-source-1024.png b/generated-icons/icon-source-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..a0785f066851a354bed7311ec898a96d62a0e1c4 GIT binary patch literal 82303 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Û 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�GSe(Gi^V)8qSzK+goY?bJ( z@iJ0ANsvv|A~LeVT)}|jFZU6uqUoi{|BY{&8@o$?9~p9U4t_4c1PRlJX95z>;xHC{ z%izZwLHc~JPk?pv&XoJ#iIKUdD>pVB?M||O%1aC+4_1^Ig_#Um0dqkZU7V-vbwY1&u9LYIuv1hkhOTvy}v@!nlj{~Fq| zkX_4xj!VlicxNlMCgPnL& z)1cJ%-LnPt=4T#UpSdO(#n-`OsHd2n(+Q)2f_iExf`JL_$EcB=0Vj4Bf|~9}{pF%hN2A0uM#o6xyj}pI18NfJ8i@1a#&=K1PV9O-)Jp6UEb`F!FZ-Z%UHC8GJk8t{TL9=LfFp*AwPS zvMPofUMghP!B^`fEeghYu1r_{7IbiM@KesWTePhlDOZTcDHn8)GqLGt)Eqj|B$4;ZI57)23 zTp+l!gT&GGs=Ek?>MR^SmLcP_|2Nyl3e7)a@}en6KYc5P(PE}LB$Z%~QBuQ7>}s|A zrf5XE+ZxGs#+;RN`7=841`!2$^^1*RrmtgmujIDP@48J9Rs1}KZI+m9(n%mJPoV_) U=}P3;zed)&rF*mRy2Y#i2g?a7YXATM literal 0 HcmV?d00001 diff --git a/generated-icons/ios/Contents.json b/generated-icons/ios/Contents.json new file mode 100644 index 0000000..0975593 --- /dev/null +++ b/generated-icons/ios/Contents.json @@ -0,0 +1,80 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "icon-40.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "icon-60.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "icon-58.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "icon-87.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "icon-80.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "icon-120.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "icon-120.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "icon-180.png", + "scale": "3x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "icon-76.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "icon-152.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "icon-167.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "icon-1024.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} \ No newline at end of file diff --git a/generated-icons/ios/icon-1024.png b/generated-icons/ios/icon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..a0785f066851a354bed7311ec898a96d62a0e1c4 GIT binary patch literal 82303 zcmYJac|26_`#*kAwAe~T)>aZ3N!f-`Dn!VVN{kj!iR{~$QHqxeW#5NvNts014cS6t zLiS}a7-KLP!|dlfyGO-oIy7#k~7v0bve000m(H@j#H z0NeOKw*fnZ`CljnwMak!0L(9*w-3%*;)Z=WH4;vU{yY#yr5SBarZ)q}b|eJ9mArJ} zn49SF(F4r~6CFy1OIkH{AKfqX^J}5*yC+?jYTg&zb=>petDSl0rDRXX_a~0WC_ib- zDhJJu$8|frpO5DV=xheYq!%M2iUrLg#k9a{~h-)WQ=PK`F-$~9a@rJ0S&C{;hjq+G+ z8AZ_!qK}qY!iXgfIm2Ku!Zb7>4o6;QSvhFQ{2cKtYV_0AXG9X$0&bb!g2?>1<^FMD z*QV`N_U03vKO=2<&;ZZ2l|K7tgLv$yQ~Vn3!;<{lPcrfC!>ZRYiVtfLyS=Fy$0pE171Ys7i6kC=N8F1MF@&sHT zyoY!Z8V!FMg{`$o7rW{3bO;FAd_`x4FVtcZMdNIk<`{)&J6zyZh(t#d!ox%K>@IMB zKJ{Pab>9E~|M2PcSOo{C-bA{nhL4-Ob|!s0?sr2X{bN$oQ}f(ABP6LIi$0aW3-|szhMKs#vuz1{exg^on zem3>$-gM}-D_39a#h!ygv*IuqBzJacNiJRy$}uG=u~)0+&a5MA?oMsKr@#?=z5)Jm zPgm4{t12AKtBX*`{d5F40l|h5r??JMKgX8O@Nd3z($c9I$ebE(tT_cr@? zf^l!)4P}=d32H{l0~-?1P|j+HM0}i4Xg~-PuVU^S92gjQ@2zN`R;z#QTOtHS$e6`@ zuUn*zZ*4@)s+}9(_e?wg(T@|6RHd=Ki`#>F-K^c6sD`!skL%m_P~gjJI(F7u1>9-N zr6&h>+LGRmYKe3IppR$xBKx z9avfsV)~`eHAY16T3e-usG#Kj=6Y`-k6=cS3g?QUAN6w>n!kOS0Pl-9kLJ{xVKsdG#e2sGFy)O3LF5K&^n5&Eh7 zg@K7NN#O`18KClw(C)dWnV`bir@HdTY&G@?(Wp{B^o_M({hWF|eg4MiJjARUa+F4a zj4&)oEW~0`O?yYKWW8qc~6HskE>N`{Q9GT9yY9mMm5;wjQ0g7`K!ikHm9nIzF{A?`zr~<%xaY~7p^dbt%w?<# zlYWmkQ3-E9N#8m%Vc_syT$r9ep&P{xISdaqw>pQdW6w`y zUU;Qss0(GmcN_-O$)4-^9i|;RB9YC|J%qVAx5W`_@8J@C*F9)RzZoQDWOs~*m$nSB z6nG*BJf#WPzmXFK_VZ^lqG4!{a0HjQO(^03+1I*Ty@!0#h76DFeR_g4E%Z?E2ig9% zv$N6L&Z*ZIKA!}i0 zQaTk}^$(>(K8`G@Xh0y~&*|xDTCs}COlW<^D!!1@eZQ3)TK?ulrd(V_mI~S8R8dy%1Yb5lQw}*3i&sr87cTrmYRxmW-C%3LSA#ph*9VBo(741N`!r z5svUO2Tuc)OE6I&-2I894NX2$B>$bfV)ic+VGT62(JEoKG-e+-QEjXcaT-AU9@z@s z5!QzSKeO>x$qe$vPE6!h#RzhU-G+i}bw{t< zvk+d;y)wh*R)S^p={4#`EBuxk_Fg$}zZ4S6#eFGy^D*RdQq66_ijDUfBuN>C9U%zy zBK{n#F<8#3iP*Dt72tgEF|df!x-~vAEa3*|J_`Ycoj!UWeb+T#06$5`oIH+Xwa3cg zi>$cX7GPI5dS$3t1}O^UJr`*5fX(wGTc45`yZ_I8?Cw^;rq@{pu-vdX$j?u#{;FpK zlFnMdY?lRy)!cD^F~UDt;CSmCF*@jF63R~1!9!FMv+J-CZf~S(j@8w#O^}Czjbl}p zzNY`A%3+zYq&RCl@+6FmTHlC~iSJ1FIV1x?NyY+^jINB8o$eji`i17}P!6W|cup%t z&XQ8#tChPm-tXbhW9eJ-Yw?fYLkuV2Tz&HL(-@bs@|y3dl`hSB-{M%DhTXsjunXO3|_Y*kx2L_R;>PI z+wKP^5_wmjKbEZIJA;BI@Q19(_Ys9leb`q%f2_$cVL%jMcchj)n;isf-Ws@`OiCDQ zk1(Zn_O3dLXff4VUQ2K?5a;8%Cu;;Unh+wgr&E>Zj`321G*GU-hHRbwi^xvbRUy;p z=$W;|4i^=!=xW*Bg_@z{j$%5|BW<=T2uwMqXPf!qR^Cg8cn$0Dh&fXDc?TXH9}H3bS2i23(a8)jnnoTbUOWMN7`19blN7KQ_`Qw)>iuI7uh$p7S~p?bhGKNajx|$$zB|*=(SgT3 zsHka0r8mH`H}HwTXq3SCS@}BA+L^qk$=k}lUvy-&g#lX@rzzYAf&+@e|9x`ton41gJGUr9@>qLdGnoz-S zp4Rly$0k1Pwsa1(M$i@Z3!S@<{z~>AYm6I@z?!MD!cPX#;Ih^oU}<7L5v>?|SuF-c zXr5B$nsM^B^E3OxIZ@zAt(Z{6eD#mNe!*Sm+ygX2v@$jfgnEt zt23#~m~PM1NJT@Kq&2QqF`$>)2mq(0$B{aJR2M22W307}UTq9(4&Vq=7 zN$y!@jP>o}F@6qv+#<~n6^;6-`6(t5n&6@;GX|8#GTQp%pg%iY5vv(h~xpLM0%5jzM!fhSW)>y4<9Iu~EsHF{QZrjK- zGc`!dU`RwqqDcIbj3_NFJu@?NBzc>;oCPBwD3^(JJrA*#<3j|6=K{Hd5zT;&Pu?BC zBjC;*K$9O5oPKJkh-ImT#!JMDboQ2~-F88QXl3)+XhiSh;ue!WXgsPGTM91G!X!J!S{U_ROq1>#Uo zk;qN-j>AUCt(6J#+}xbHdBPv#x0MapeO!j=LmuG&ERblBMY%2w8^R!A2ffhCEoZMI z&Cj3eCM<8cJ^Rjb%Q3M?$9idZ4?rLWyf#RC8S!>afa;TuMaHS@>IB{s5SdVix%G%w z8%wTHUr_PJKu!^g?K_a3^u&i(O3?(hzMV}|i>$kw8_g4B%FQCUgzBc`DW# zt!-}T0-ohcUQ5e`bVE&A!kw`b5IR4Mi0ND$8F}{!bekv|nl=N8H|Ftav)EbuN*KrK z`gk&_tXyD@iMzRCw@qFwA<@o<&gg|Hz7Ymj7DO6}j{T?pJjRH%Elazv(|qR(ajscU zN!~VUJunBq`@Wz79J>XPj}zxfqlEgKJBL-6j#*-ijhBprDc+x?rp!7w++~sr>}hYFJyc!{%gwrgDc4`W^B0 z>*D^A4KQ$vLf}a>Qc%1f{Y>br-v!A7k}A$vBLnOwLcX^VH@*2Oqb72CfvseSJWEG`JnW?@HnuufeUImz6|;gI7w-6GWq< z-QDkMr6b(Vr6X5Xr3h;}*O>w?h|{>!2e_Hn(F-j4$b=iWaeY!EJ4c>dcsmQlYIyV5 z_$d~MO5dd0(0rR|1X1#deffLc9?rn)I2i_;;WOUdiN9K5C-GQVJa!_+NGdwC(rBWO zcJSr_%pZ=h_EnnD&trRrh@BW0>l{uU3AM@w*EnH61m8cW=;jz+*sSj{Ay)-60ByL5xKKtA$qru4iDzEb1B)cOMb( zP42*<7Z=1N`E{hAsp{Dz-||lcZj~YzA|Wn;LYl0=p2(4?UckjOLXjGKIA6}N-z(=Y z6QZ`?qzyX^M3dkcbjBt&yWqLO_GzZX?R9UpPB z&CO)7)7fvZzTx3qW}*_W4D{tqJbsjR%3S=;bAdz8kD&;Rt}p`k*9aMWs4dI=MW=tO zuGc=gg-(Us-ZLhqqojNBW%bFyV+>+&`-nK5aCh^g<6kp1cP))rvqgLpJ;xE%BS7)(o59B&A{sZhQ5Y? z(Dk6WIC0ecbc;xTclYZJ+6Mcw=R*103K@NcNQVH1^Mh*-Rd+qV?`2l-}t@m2>~N=sjlwB?KR_x4WX-&o8;=blmGUHt=0Pjj`NO^AXL$gN)d z@yJ;iR9+zmirDy)Jg%fFy|H)T-L6cQahqevB3c$A?vHXLiN0VZINt{ogx#X`^f`yoz2Z_ zkT?`=8C>rqaMz>fA4Lqom6%@l;O7OV((GC5i=~*ZR5&t(H90iNCKjuZP}NkK0DvwXwd;^z$w*#7HY+aBkDDfF|SHDvLq7MzI%i$tJdJ)`r}SY3fK z8W0yeY6!{;#612G;}+}f@hZa2D`I2~=M+f}=EPD|Qo*rAYa||3=n~>~h__ap%z<`q zuYFN?P=0;%F{W^hQ&JBfM^UCBqR}nrOiLAh?Pn^bD%G?7U%UKY6-N3!U7O;~uC6Ya z?>D-8-s~?fM#OE~TWT^4S=Rd?!42 zv>#GW@96JD$?nZXx}4ToEM}IO`^Fb90xTbix!-5fRYuVyCI)0hZl20>bEW zn;e}_nA2UhOQ=J;fS{WoC3&pj6Nnof+Db#QZ}Ku@w+6Q`QJLv2o-dyeA|00OPPBAI z*z6PF*B-}QKfKsSB+o(=rS?v369rO?*CnO|`5B-YnEC)e4ahA1Y*4xfd{__>1)`b& zSN^35ZQzrM;9ehp+JVCYas2;e3X>4@0mnp~I@r>?V6sIe$XTd9h6Ft$DX}hD$b}!u zp$!f_In;4&Qlmr6pI?OB@P49iv?g(OlMllkU^3VBWoVqtvy7+%6eV;AVU_#Q`7F!K z5$B`bmJlZ@s39tFAvPZG9b1$*eo_F<^UpYQ=#NhRiDtl6cM{&!rgh-o_#!6U0ym5H zlj_8BrCI~F9p;lW7jhA1?!X&WLxHHV zMOZ(#f)6V1rLVc28gr~jpX2`iRKLiM^5Pb5e^OQI#i_0cHV(Gh5HvfDzbm*uHBUB(W52g0E zxTD!GyCDtqZtsS5N(Wh9E&K6V3~=tefGbdFug^sPN@W;enV=D$;b@MQP%7vI`|yvG zUv?(#yR&Lsrc}*%U{Y ztX*2$TGBV*ZAh$^KYSOxW^f-)su&&wTY;I-4(czXr@;myy2ShbM|O43D5al46ahp<|LPgzD@ob zm*Hn4;~pDl{Bw6Q#I|Rpk-jQ@IdU3*RXK6wzSaSLiKO*$ib*v{`)#sxd1P$b2A@AC zI;Qw7Bl9J(=9C(iE*u*;_QYLg7f=B1=enx578mEB#&#VyRYXC#hS!8E?YI4r5Yy~m zNzcFux4e@PHSN5w&wSkh*MOmEasV)Pa-DQW%j$?W`w zjlCfP+S7*B=ZnA!njAJXG<5x4T@dx%5{A~gnY(z<`3#_|Cv&_Vkjs!;DRK|i zv91MRRQGNO1$$h`n8G&TSKr(UEPHv+SXi>CwsupXnejrWK-=u>U`5MXTR+Ay#2+la zaX44eO!-kT4@K?9ZbcTmldz$h`erI1QNNS7hzTq8>ud{@65CnG6gq9&VC3zpE9d1C z@6Dx2FRUehdYx0erhCe8DTRBjoV`*z^!1KcGYIBKnfJpKr}LDI zLetu_-i&7*`EQ|rnvw2WE5R}f)wanKLg(qGa0bi0PZOZCD8G77?&v#spl=p@$etWiH+TT;Fkh$ z&5XB|D*a{_9>#f`Qhd|vY}@vvD1W(@zF6CU@XPm2zhVhG-ermtzS)?)hhC=f3C!t* zLwrFerZ`>+waMWldx-7Ph`$fI}aZT5mhe;t3 z-h74(rm2qkpK2HNzq=K5SLU#(X<6j$rF(NxQHy7+8=m7jcvS`OZ{G!98~hp%e{|DW zb1tpOx$aX3CpD=UZC6wf(Z1{{LnNf7P#qmG5AzJmK2;6boGxX7%A((Ti#X*En z#%@zSc=P9L#TF`{d4gu}f9ocAG`sX_2k|~4kiHl+hrERJ_H1l7$Z109={~TCq;H{iY1Iax#^-RcLB&FHQeE{aHVJMDI1T zDry~(J-mz_Jq?QKV8+iMbU{tnSE<6b4`T@*Q@D-Ttl#%|CNXp!B_if|E$(L-G|H|2 z`J+u^UZ^^5&+jieT@!yM*YDp9T3-5&m7fSyj!CK@-}o@%AG*=F1&+_9Hm(^lk1DaG z7Ne0T90Uu@#~mtm)ovG#i0wozGO=3*TPWuIXBvJyoW%QPyJ$R*Irg8uJH*p_){|v# zkB5+0j1dyQMWC>%fb)*ZT+)(vjoJeUHTBGbgJ*PLep?Ofhl4+FC4!p|1VwE(OsZXl zX;)z@5^G_+a2{uvD4vcM}u~<6Uwz1fPX)oCb$(rQbVvv!ZbHv-+4#RM8HP z8_20E5p6!Dk*~w3vGM4Tl{3=tPz%>>H~JrMZ_4R}QA`E@9ofZK`|iHjQPwM=t)9c@wxhOSYY?VC1%iU zxsHQX5EKkF4sNCQ!;SjGQ5J;P)*9r}}hJ9N-#qH^Wpv=S0;xKwDgZPc)12m0=GL;I~@)toX}1HM_Y~ zFP8?hqDij@aX}#>=CMG2w@46*xe$$I!=>Sj3rCEYo#slhKz63m!fLymxBUM+Y>dkN z!8T$n;U~h`yZ~7mrouZog?D3NZ0$gQ_wJr!y%I<5AYW3hDWDm0kjD49gXR4WbFwQc z{lb7-F)TLZ{ndj&y*0<=HP?Mcsj>rGmBTALC@gDq?TVjJ+Nq(s!-uNIEITQQTd1k2 z&MFiYlGCu-3oGUa2V>-n6dC%(@z{I!dLE0C@4pUiI<6o#Hsqb74X4(Z%kl%{2mh(Y?PD8_K0m;)L7?${?hE^#|kM$Qc;8+3o07e+3wYLQtljc4$MnX=1PyD4&5uf3e>=z-%@S<7>ap$L=rzZyoh$h(jlg$%VO`s88|QcC_T5-*HdBySPK0<5*Uo>=`}dEu{NWd7oZIO=hMGW zB^-O#-+9rhtn7hEbO(x?4QJ!xi~ZV-P6+55?!12GxNX-+6_Vnlq{j!LI)(d`8mp_# ztb+@uBh$dH$^D1>$r?OybXWKe+*q*j^W6mlU^8ASD@?TYjr>P4K7vPE9!lh2n2WZfCv- zjQcAdY#REiSFms*+W{z0S@|258GO}k(-hz1%~@>r*tj;)3H#3$|IB0N36$>Lr)BxA zBffz|M=IEWVqIIS!Q)5?@ALipu4O0(-JTCln+T&cV{GV5ZFR4?7{^BKEQ?&DR$g>A9S*U; za??;9H+jw>9B0+y=a&L6?upo&t4n%jNs1>XI^pBW6pa{Te0^#HeM=TdN@7`~ZFuOrB1SruYG(w zvl}j>q0pWE-p^dTuMgVX{zT`@i8>5JU*?r2-ziI>*#epioDNRPOU{7YBSgAJh%iH_iD* z_YHpd=<84s*uDHY8ToSm@)#EM3deQzHcFW5Ay|xF#w=3;vDs;g-CP;j4dLkVu}X8T~|KDiQZe4Tru z8@8W5R6HGatp$nk#01u4>?fTY`&>2c^LWMkiDGVG{No%RN?(ERcC($I-BILe0=F2} z`)y9Z;0|CWcDBl!kW+XN^v=rW%>*M1GcDU`>Jg1&f14)xAD=^oNqnERY$uNNGGpQO zAiw0zpV?f#>3CiEALR4;E5DvPiLRhFdSONehFnHu^j5L0vQRen9S0NIH);MgRPn;` zZJ0YE3%7JWdVMbqNO?HKeZO?>7oySCtqOf1nHPrVVMN`0AS2h9w=U%egYA8w%~zfp z{IL0x4)Z#nF-Gk-gwij~Aa3yc`@mV)n+FMD6AD?M_qv`umg`^PYnV4VzkETv>wk=h@&)rHc%x**I_Ub|fGP5yaX*h1%0t69 zp8ERxvevrYVVgsJdL(K6FIvWbVo`tb^uu;AXcDnS$^m;w$ip}WE)bfOOH@yh(n15JiKPYqk$O0^N z>|0AUiD2J{<+t(0?sx2+06q76FvKeRq+BW@QZn~nn%7ao6kh@O8?Nw^nWM0bVL00f zwX%_@#C%Qe0@hzjzjr=(C`OMA*Jq;G-QHj?jV6bh=4D6UE3|h{IP;%f5$;K{E{#F% zh~U6YX+<7a2lUWq#_OrNLgR+TQg+Jx8G|EN-z8S`VpMD!!S!`pb5%7l*G`cR9U zM_%pSy#8YIwu!RAa$Z!B4jkTn!*MJJV!{39738`L=vVB{nA^jK_r8lwHVM4P*A%E! zjb-Gi=FxyX>QC-k!DbAN1; z`>Q!O2raVTQ8hOQy;=B}&^gNYx=Q4-@8UOiXE=`5P*FqV1`SWbvi*$0gHZ^7+MqR* z`D2Lp11iyUPr50`gwk#Yx>))V_MTa1pN^D%rC~d#^&~EVd@g@hWh^N=LYo_it_CKU# zyl)pU!{fI^^FK{GTI&eFJe9~B+vD^m0UcNuzZP$pA{uIbti@j^$n=<)w`CoXh9(xA zdX@vnp5Z7KiE`6UV$No4 z*T4?q(+51E$xjwesu&aHFPz1jQh25JgTOSJz&3E@eUhx1YVpHyFxCmraZHt#esnHV z%ztc>1DFIaL=n8UgiZ|71RIQSC*uMS!sjs$xP#Xo$hmsyeR?IRPEWLt+O2U)V||WK zJlO@ImMNIchjuSfT`6qJ5vLW56P79YYP=iS&8<7i4GJKvzF3nU3+)Rxw7W{f(B#^s zcTEM~G){-0IE{%Y2j9P*trWJ-Su;bPHs6@x z*`+bYp6!I`9qI|t~c#OLsO2-u@W+=u&pjUsYQ0Dkv$_RrKg?R00bkgt$R+;(xX`v1@taPFP0 z^3X%02x7m)&vy)qb$)%xkK4JVQmtSz=#AxK4E-qKSjIwcON``utujG8YDwt0y`!pb z;p3J=zlo*@CWPN9Esaz{9F+3c>vbx`}05CM=j7C_8 z*5#y~peSxe8R)~0=kjHO1P1~2XhHRKd33e}8d}X(!MiXTLFlXvaPZ>>Ecxm*fZQb8mT3>=y?S+sP^O3%FE5_g?`fqbcvLs!LM`IL zNv`#0M)7H~Zeo&4vI&#<8)a3gs394c24SD~CbO>T5= z7;T)yTg24`2DMJ0mI-pBiYPBIs*UX4hWYSemN+`nt2S1Sid-Z@{1PEk~bHN$Ub8=Uer3%2YhJDP4e}+m0#1|GXnrC1b7ozdcJr*fxcd?F_Ec*KO1sr2B6lBPK?YCgDRPtZ zTk`We@7tmm70v0GTkd-@Y|C*xUJ&+G*kBWj5d4PqZFdSri^8; z_9iNnw!grWQQ-#-Cf^IC0`n^@iT4Ue(NCk zAVN7*7!vVLBVF=#{eK`DFy0=?*QNSf@)J?unjD-h?nAPYRgi2QH`qb+`))OptwZ1@ zlXAjW=C)QQvz5)i4gtT>bG)U>)ElTB3BwxJ?i~($h!2Zzgx$bYJrSj!oA*;aQOvEB zdhA&XI?kO{)8);98EsD;+j&D?rfcE@r>FMsf3yOZ?Vqev`o_jtHqiZyKX`TUc{kz; z^WI|nz`4QO@lURrXReRUZ}Z5^&xH<#gFHH`<}3ErAmymEahK=0%p4=y(iFL&4wHpk zKZ;V%r0v27S6#TTu-4;9VBf3qGGwneu(wnk{*UO6urW`#|F7CMJO4RVDSWyVrJhae zNfp_c&_E{_Q;syU7E5P7S54|GDo28h#YHWdhc8g0!X^7mKEWbPD(@{XZdd*8qBGL7 zT<914nyX`#rg9sLYVkw`})Q#8S27o&W-iUKQ6?is>OW z{W$8PGRDWaiuIiJ?tUVL2rjh8W-8Uz*JoW(J^deE0*wEdsU}iNyUD%$OQD;t>q?z} zxwWyXhL1q~O{J6^Bz^U<1T05h9Gv#oH_WnBCU@=pN+<_Uew9+Nz($F4J&NTD2*MwY zF;0XQ+$}Esu5T-4tc>xOD2#&jGaDPih{dTe~jbQQz$q}*Y^D9;r0kJ5}rKt$(67xqRbr(Dg?e{bK(3X@WqAea6(-bke!4*r*VSX ziSSEK63CYw!?k;x`aS{~Y=`iMF|Y0`z{r=zY@FJ@y*Ig6EG4Y1g{9+_xN9_PejQ@BTgi5>+{bLb{KydaU~LqJ3ZgzPn+hE_P5Er#qKkdP>YO;ayM`N{ zeQr54^W2yF*o1hsiYA>;-VR<5%QNT>OBh3BVAp9{V`b>&lP$2%=(^(_j#4(yDe7d5*uKho|P+^bfmc2sN2pFeV>eBXBVUaI(31L5F|z<@yZP zO9eDxDJkd0XU6potPG}n-TZ`&9UxdfpGcJi9(RISFM;93s^kK}A^5;cn{IMualdETaG))3>Vg2`xX`cgWyrzYKc$%aw2${Am@gu% zJ5pi66jK<-piD|o-vJnJ3^zzu;bmS0IpofV84T~DL^?dM&o6=md&BE!k=TT?gINMy znAGesAE%SuJGl*qB(nk%3AAg==2ezY>5oEr7KRfz@ejZH;h)t#7>OZ2V^xJV!)C(O z1U#4B$L*AUE#Dk?Y!(>^krM0?x3Y>eJwqpvb_#eC0`*mbu8*i;>iJNkFtsQM%UZP~ zxsy<==17r}4b;L%f$%_wn(W~v3=vB$p!Wvv^@Qw)T5?HwW!TE`u zd2pTYNuAp~^G%S;eX)V6gP-hpokyvxCxpY&3Z`wI&8&BQNUHBfz7%Y&m5umVPOvN? zlbN_?KKk=QBiCCXHMjR1@~K-QWJ_D)d*BGhYTn@gxl^tMf8;qvp}K}pgYdOp0yz4b zd|NO3Ox=gDg-0V;@9;IJ;!Y1nYK0s4ah-RG+Ux&w9-&K_}bQ$tT+S9mnN^F9Yu#QI^sL7dN@ua(qt^A+6W0<|O2$Auit-u`-CyO6PuUe3w*S@^4~3lO zqUHy25^!ging5}Mh{>acmhFzI{lv^FBdDVQf=3&40AkdrV1IGwB+5I5G8w{~2<^e$ zshS90SCAT873v_>6$=&QM}G!6iyk5}#ZO@L{(&OGn*=ezhmAhw#&ZwoP>_NX!&a4K zz}kPR3IryJ<+%or*G}S{pXDh8YlX1v&C_+S2^=e{2UCN=|7s)PyPtrry}+@MwD2hP;nQ<_OC^8_yR7p%IbmEQ!?__h&9a{llFqf*TKe}7qlh_R)KW0bjvD67 z=ZVnF_>!T{WkFfB%;(+PC4UC=9l))rU4?--fApFC5y|Fylc@jJD&mVhU|wGiDwNwl zla7Z0FDyT81!0#{Xm5ULEXLD0482btIR+UWO1L%u2M*87%ErFufLE-sCBi8ughuDL z?a=6m84I1M5r5thv#Y6E0HDJDd$4ivNg!W1;?D&igKzc-yX@h3uEhSkVECq7ITtwH ze+{o?T)bme|MQ3_JEzqcT32=t9DC3*WG`-J0xJ~LEZF8zTUT)#a3(;m{~25>vj-OG z-Z{fI0>_FPz`v4{`&Jp1+v)C=!2Dmv$xYcc)sB;ZT`fZOMeS;;6mZ@AD;fMsVr}t{ ztzN?3!)6y6u5N!lCQi9ynRoMu0%1C9%go@5TJ;K&s%T>%QXghG@TvF* z&6~pQ$=RFxk(0oJPNU97co9V0*glz-{WX>Lpo@N$l>_kOPq2jztmJpknvEb=A6_4>y={0> z3dmTT4|!h3G8^OGINf*>Ul;g|&I+k=u_KiPn}q8HdYKIPW*z+KDCKfZZM^m>9W7xtmu?>AQa>V07%CXV#V`rxg3&`&8>2y)e4Aq%x2sdim&Si&^&< zl3@Ogo|>_~+P5{w&hH2SgEU}SqRQSU3geU$V=PZQ`Fwx~=e6;ID$p}^zf24ziZuN% z*N$GzQ*zYQ1Jrd|Z;Asg9oOn~`%kYl`T~`nxpa8|r>t1!3;cnPamSAzVhSk~|kDv~b@^5yf4@pMw(Wr{;m9y!F+z~mQ9xLU<&QFil31Yv5V=CjB z>xz|kE1jnwY+-ik@1{f3qL6?TvE$VAgmtdnML)LAxXtp+?I0U%mDWynlB#T~V;`q$ z-8Qm$+~fB;qO8kj1M%e?=ZE4D)E$@5Q=-z}xwN{(xrl4U7x{VlumdT~uu{P!TPj#vmKpIBGmbrhMWfOTY!cfRtwA@8OXD< z&VgDrfQC0%5dYn&kkODXZpc5Y8)3_vU9=|ppqOs_L1fy^0G_FODYV1s_yvc&Z!emOxXqni{WYaf+|9f|2!9(@LX|tc{EGe2ia4Kb+ zXUb2z!Wv$|l|(S+{JKG8^VEDWGzaBT_TFZBXZwuabC2lNKjNtJBY7K6_`eOrvHH8h z(^ReZ*Q`jpg(PX;7RS_%hh|z#DBRLL-D?!ugb0n zZ6@I^b=JKH?>)&sOPq*Q|G(;eD$c*mFvm)nKT$g3{8r3#)Uf73*FI$?!T%grs$!&4 zWhWBXy5X-vL8+JXS`&ige1%__pV(3bqBmxr?6`Y)Q-EB|g*F^|T9AP&xb;Fn+e-Kb zyX%TtBEKmL?BT{AkDQiE?3{giF4KX}OiBnCv%KHKIOAQ1bw8mG;30;25(bpz$111K zO^99qEa|YrKtQc(q-4Y^0ka(ug_04U>L26@@l1e6De%;U5Q~~)S&|W#VD^EYKCr!l zxuy;a&ulMrT|C}0Jz(jKnqf#FsF@?pKz{W9QFWDJQEhE|&(H#j3Zk?q(p}P{ zh#(-H5`)s+ARx?mKu{4-DQQsY?h*z?KpH_hMmmSi$!~k!bFTOO_7DCH*Is+A_0+nb z=egJM5ZyRI2=j|K_rp19rP~S$28L&zgDMsS{hM`V_cIJW&xP$#h(9|?IU#m;cdvJW z?XdT8`AoW-^Q_tFAqqcpPhgp*Z`;g!LQ-dz_>LXY9^pZm*_zywfHm`8HU202R zKGT90tULcIYc*H-1Xf(4dWZs(Z*L+~3C?Dais}>|iZ&PD5!oq1_pxSt|LDV&XW8>V5oCK7Fr zD3PnloAd(?8)*64w>5^fk^Zdo|ESl|gIPlR&pRI-_R*<`iu6yLA6vZZ27VLjw^bA} zibO9&wXb$A?%`E$FqODodOWXi2InJOOkGk*2z&04r99ccXqZXs^GoH(v@=GS95(vw zt*o1F&2?ZJ?#D}>{j`05$vbb*ar$Ofk3MzVaU_;txN|y&DV-MWfE(3Ur+h}qPi8ut zDV&U_j=PWOAzu^JwARBy)=9@YAb|899{=&Xs4yfVPkGt`3%T@ zF=V%2aG;_Q8qQ0{<;+DwTscG0KAXng-NVP1vU3|z-gCGUyW0Bevb;hVly^UDbwkQO zQ%lB4UfFWTv7C!H(|6i4baYvC+LrL;tYzMaL*?n+a?e1$jHmsj&gI7b(S9gxdj1>|-GD z9I!jqm&P?N(q?`qLu#fdy9vPu5JRM{w9cm>wtUiphHowCyW|qj7ZtY zkBtJH#^3@$vPR{l3`iH}&}OWFD&)-zaN!>Fk;AGphrP#T7_;r=#%_|za!LKi#qami zXdyq2RND!M)000L0Q2(J{($_)02LM0e( zsHk>MWjTfBAa8H+L<1Ll<}JZJ`EW4zvX*}u$2)hCT_)!!q`p)7)0M4QAtnvEupf{5 z{T*H}>s6V#<<8I@YyDj0i|DdX2VHr=tqP4gQC%E9)TE7|rRbI^zRWmr$ z$~U|RbaXz@3lOtWTYhpItJW2w^ycUVh||xqOAK`GN~E&SB;1K4F9AK5 z9roRJmI3G;CPfnOS#EQK1D)krU7e;rM$r#?LFDXXB~amSdm zz;?2u^7M=hc7fdCQ$+YfWA+Nlror0Dj78cbyd!4VHLK*5hTa!bI<9L&3zpC_h~;^F zcs}>k75?HmM^s|~l@Bs9tx|c=fz~6G8SOl<5Z>dXbv$C9$b_i1So74dm+C);e?g4S z*K?{rKlF5#ggR%{It0nL8a0akI$S$E+=bB*6VDub8=qYaQNkYNk|&%#xcje-_;#<~ zVSP~7M3{=NIEloX{w56f`}c3MSu0}P1cJS9s!H{)ZxHJy<|P@SI-h-J%>AyC4jRKv zh0F;XezPn+N9u7q4{SC9Y^wm@q+9)5UgZK#){T8}PKpXSAKbsH<20pvv=PnA3^Vbl z-kUj;r&p1M%}$&JFB|EzC~V6gqxM}*D@m!i+I|QZ>sa#imw+vj3kfSfgC`y=1L{;h zimhR|y_P6+y{BB8-QYgrNh==Rz3v;|eKV0HN%c=`PW`gO2N}QqwY%GQb*K5Lz4t-r zyroLvBjV25n$3@W1c7HrBjd_i$NesrLI+kt*mX+wwppfi8jrVko+vwfIf{(j^tZax z9TwY#Dm^H&^5{(mu#FH)f42uMy@;v+kc=u=oOCVs$nQ80wch||R zmdz#y@_Qgdq42d$!ROQV@$vXZwL2*1f5hYWIsVV-R`7fj@$peA%WH)|O^l|6T_qG9 zb}-F(`zjkD;QdX<_-;E-JmT@4#E44=BW3LU!Z%uDQLXGF>MbjiaFr_}%q?FUAL|Cs zoK);|x);baUyw26sgDVgF|=B|I+wca!kj&;1D6BpP(!UashHa1bTS)bcrSr9PNn_G zWffXr?n%JiQ}=-+c&waa`EtU}M+Y#a6z4hjA({zVvTuZsRqD;1Q6FLn=*chUTZsD@ zHb8n)c+jdsQM8Z>A(l`*xwY4xZ;C!)CvA1jiS&?FeEK^R$3-}Sv5_VwC(pXUq)GO_ z8bKZJH=t}>-90}!**!TqANR+3V`TZ^++6xuEOhVh@iRSQH0+xCd8EgJCRkdkh!b;` zr~zaT?K%12C-T96-FRg#bIe#3z!FVsUE5q^-A|eL{ZMw0Mz5M!M0eMRr0cwad`5?u zjJsI+mlVSRx(0Q;Gk$vdY&NOtU}CWUs#?AE-+ieotpKzvoy}bAfar!U$of zm}$rJp9(?%*6?HUY5Kr#(#v2~cE)AMMCy#AICLlMY?jsK1AhCflb@|3$4(51_ZY=r z){<4kD^B{3jneji>DjdvlM~HmB)hqzeICH^9?8lU*qUq|ds+1~9T*hsv}~XvBmgCt>QRM(-Mz zEZ}#^L(CyF%!;-&a4hz{*`AQelv`!V)E5dUEr*p9Hc5M6^x>KXjh@` z*A~&YSMPPly8AjBbq*=8sL$Dw1brNT%?v(5NOPzJ_YIE%=i|;NFsjIrY~;GoY2;$p ze&`N3NW1(48v>-y@n9HJyg3}yMmfoBCPa2!hEQW(=B*M5q_BPFoqP_2-#mY=^WPTRe3mV+_!CE*0QHR zK=fhhsxz&-p)km^%-Upi8b|O{ukb81c%7m*(jGq&{uTV3gFz4UoHS4psHf6u^iM(> zfVuJUfTSUdXL0p(z&guwkP%9c<3n<>b=W!R; ztXUoAxh0Oly8r=9WX^DC~rynK~&+ZEA& z<)xSt>FK_HIR-n0=KgkD`J}!{ud`O<`3DTkD(u{6g4MNKMQKM`n@9Zhgh?41i(a2v z+98j8k4j z-xh8b2^;qZ95!Y936{)qG@jThnYyPP{~#RIWL)271M}W~D80nA2Q9%`GNv&n?FHFI*mOI0EG;dck^a`+YB`dZZ@agIvV209y5tzJMZfnj|Cug6bnt_Ay-MFj zF0Bv?irMWozMZv#^%=hPwV)09P!)uGoJu|7?8rUu(aK zJT@^XRt0NMgs&W=Mo?hRvU&zz1j(5z-^W1|3DpGX!o6k>4?wX;UCA4KymUJ@b`f z$%yM(Y!L+d0!yq=I}g;Fv1uxrmYnt0p)1ytmh4%JTUB#i#h!L=XDt+HbonBb(?S1| zJhq`9hOMreN$sxFK2h)A7nm-jlQwGHD0f)=c*<1wPyuR1pS?nCI@-zFmnjSkp;~{& z29_L4qsizK9X&uXE}PtNO`5>gs*Yis{BeTQf+ypj8c{ z`t7)Y23J^3P2y{8ZUzJS@GpCh60Y`NZprKI)U_6O8>8=ygH#7wAqdHiQBF=Kro?A0 zW%-lDX4WMwk-|Z}pxgw1%l2#e)xBM(^mjZzmLZ-c@et*)JA56bOocFBXxgw4o z0SoKT$1cssd<~?z9wsO(e6gPSF<_vh;vEnWECcq_m;4)eFzjgpE;1mtqQqJ-$-BZi zcjZA5V3z$oaM0oBhuoa}iGv}rW82+|WCek5F1$$HCygxNscZQ1jKUy}=u@Q#U~_;+ z9UEEj_HW32ZzF?u0fcGRjgUT9(_cC?Z_$+w3MN?&i+7v9^w6Jt3JI48ot1AEmruf` z9pkkuFk;)o^&uh2xJ~I6RLtUsamBP;5@GvK&AdU@Ig6n|k5LAOSKN0`?s}ISX|6Jt%4Fm*SMs&CtMI^k zsP0?rcDa>XHDP@5ZZ)^+BFOAj*BH>4v_qB`mM-l@S}|8Kg_DAp*E92bu$sNjGUr|6 z7Kb<3b~t-*5ry7q1AC4Kjl?fCXNcD0+K~Ez7g!8vUwy~N$3sj3r(-AD=K&ZeSv$)o zG9fM_!ETA3#5wk!(>D~?jOt}|1Qw(9qS7`32*Zy;1wKzN4p2^+hOZOF5^e~=OIqBP z@^D&)tXZlo0_uV7*y{j~`KG{I;gj)FxTni>D{@!cWgBbz%RD;L6+h>BEg z@x0^l)$u>|)FD95!p6+mwHw-a)$50A3q#Y)eCC0B)M6vvbGVTZvX9>lh(n_7&)>^bllWKo0x~E8M^qipQmo) zZc&vpG8K&}>zZXzU0=ohcvX#<$ zih;=TW*3Y@T};ly#klE1-v@+T@vi>k@lw>;S&@@^_jq&VFDCl&8STLKXMMpe4;15W zmCb@(;jK=SWiu;SapD=Y&uvV31!dkGu<06N*#S@BFr9vU(|?E8VY0C6_j1Lfb71Df zPBA{q@*1$Dt!#2I`mbbW)AqU3RHtYp1g8__2O(VV?P`Spn1)|hvxR73&PH%tO$?zV z!V;)(lrrN)JpN4-sQ6Wx_j*()zY3imm-YUfYkj+?2y&R6j$J)-f6-++YnrueLp+p? z2fo9R?9fpgCknR}7pR}sL5#QRKoNu8(&ZVQ6EhBH^wVAVan~$~Fb@0Pe)e(E9&4%8 zCyu0)R8fjkQmP;j!N}=uPWz9VlfPt)mLpk$wP#OfYJVUm@(Am-n2nb$_yuh0F^;Fm%TXfa(Tf)+h74)S#Fyu5-|$>`c5N(naL+s9Be|KUi6M=iiC!hAP-wNgl}k>lR}?|A$@tCU>|Yc|;C5 zH3wT2f&1-2X{bMotLGKE5|_k$1hezx(D1Bll9mWk%onnxT*hJQFg^c>=53|&K&l9F z>@|R4Kk>{hW)zEPnXUr)tnP!oat@sjLv$Or?6wft4mU*|dVn)njE(b_OOb=Y%gupO zu*CcCXDD!KM)7HB=g8!)PjHaeZN)k+LYE|c|GRg~S9aZ%EfgqszlXnj{;L~{fwQ+N zh}8wW2)(+}N!th_IYJlFGfX&s$w22qWY?$aEXCVwN+Ev(ROji8|LLd(INllbY<7d^7eD-#S2}*l zD=&}gfzKLPG|a%0V|AMkvY-7DD5a%@elr&VR+m%niXOK(%N^~cViZru60R^!9^`%s zo7IOdu^O1iioIvGX$fk#WM=9g66K29I8L+DibFM;k)~J!rVm7=H;c`6n0l(A*B@Mx zGp;79BK~t)Iz-NE7YL$bkjj@+m+M(E$NA)Ki|6M@QY?Wv#nJ#%GOtJnlJRlcanPEJ z2q3v^H(77LXbHUIgU}_KVrz$t$?XVMtuIWJqyx+0HaBaO-&80PmoaOptMEFZe0Lpc z)kRd~$2&AZi8m$p3>{qlX&ON?=#Q-H(!WSRuQp$w<(6VrhsUY=%Won~xy>KSik&XL z2)oqR_RCF!3nP3qv|STt3E*(~k42E_$WP!JI42vz>dj6xk#6-E_O$X#6(W`W4yC5_ z1x59b#lJq@>L|}!Z3f8o782&*?fFbvUFDzF+V1X|eu{Lxq zq00Th{puqySBy6VL+`tZGI zNMbReE+O~+79^>bohb-7JtUZ|wuxmt{Nml{P(-i9jPz6*^&sRa{o>*HmVRSyx`@fj z6e=ZBpBIsx!)?>j{K?A-74zI8oAnruWE%W-w#)uSmX*YbnC zsoTu3X*c5JiVqko^BuQtEN+CISCWN=bw$cSr2hgahM$b`i*4q*HTTZ;YaNbEz(a%3F2ll#bpSYb^dLUEJ6N>h0X^VodOMGTn8>IcnK3K75sS$dB?+s`! z?-lnCqc4@IR1{w_HzL8LnY_zfH_3t7K>;XiVsiwg?E^#tiU7#&|6H zX}J1UD9u8roAA$+KAJyoBD;P#opr?eX7b65{mD-BDtv6J)Cf7oQk_ZK;{1OvM-GKw zxqfQ~x437is&UmQ`2jKeIU*#WULvnS`v zRo7Rw>5{SDR=hz0IFdhfM+p;mbg0A zYr@nTQ{ia?Ochh|S0SbagW?vM#MkVq2dAt8@cNnUU}A6V`1${tp&S6nmlSyIWJ@Lz zqyjE{5`A(lB}mH3vaCyO@hZA(zF;w98S12=%7c^>0{5<~hbo`^$LmtB(GUwBE_j)% z8cfR`wt0L{Qw?$ZW}0Tcos@K} zq@J(meaO&jUZL{Et}7CeE(h&9a&>A1>Y~MsJ)|QOfXLo)-UCa1T|zy*CbDGOou5;C zmGXzDe$E?#;6H9)_;z*ZgA$Qa}sS^hz_y`E8XjQ ztsOO|D4Q7*)9e7^K_SV>f^5swL!~P4jH>m9n)^5ppOWv6PEgExpZ?5{HQmS|YA?Lza3GVaR(P-!@)X7wL z)~CPJ#72uI+w`IBV2i|Oaxp2QX4gS5nL;BZVx0}b!{c<0s_&-|4eV-QJu=erX8T?t*IjZzA>%<<#;@i3_TT}h%z}NKp zy74>U<{r#Ir30z&)7Y5PT>Nqgc2(FEr?^~Hn@3$B@Z*edy<*j3H)^M65nZB8*snfu z{3$w!Dk-S94{FN(S6S8|SD3&KJeDW9X#}?5B!bm`l6LcJqcUp$f!>1wKW>*C2dG z2R1pZg4x*ILssekX{Q_zd^aR6A@x)T^@~2gvR zwSkoQbsm>t-INonR&AzT3XD)Qw z2sK*O8MrH~?A|7{i5d$Kt zG?C4t(7Ikx_d6ZqshstLje45>qf#Hgx`kW7=dkd5S2p3;=T|^cX8Ipf2)n=tw63KW z54)mLOrLm#Z5+j07zJ9Av#Z2Th~_)7avbiJ*lyW-i`7fB2tYLNc03$6OwL%%bmW1x zCS>To5ro`hEGBQT5YDzQL%t5+bJZ0#98?&6wOSUWi#n!!9D)zrs7z4EaTF)HthUyl zQvG+$3rYd5Tgpa<+Gm(S_*ITuzJ8de+Rs;23c~C^Jj#3Qf&A?Qk(&8Bd|Gwfsy8_Y zO8|s@A$AInOxs4?9PK@T#bOv^vPG$Ofq7X)B5PYRciS5Y*numRC;j86yS-wqr>Z#m z8waP!VcBZSi?DkOXR`O4i_&@ipOMU71q1i-aTV@8yn0~Mw_{5`O54S6hHvV?Wnu&;Bk+FJrkW={Q} zi3c*Ns}$2Hwe&y+9yTkqUPh&HRqmo5^y>DHQ3_j^M4)9qgC80YD5m`|8RZcr)A#C< zQclRJ!opr))-nRo*pDl|t$=bLL{R>BJlJ{Q;Z#@Bi{-Deq0&*U-v_4JcT!|m&Ms_A zbp)&V)448w+eU8UL*0#A<^!CVP0Otpi=j1nH{>Au3gpe2um@+?k{BVID4*4@UM-_i zerK{6C-k8AfmXk>PiFmmZW;0$|0=+NB=M`Ram!tZ{J^OGg6=?ffnFFI!U+In$lF% z26q)Lc<@1+^68M-N`x{cVx}#jdE;#Gf2=&)3P7#csT*Gw)Yn|*<)u6NqhNZj;r^qe zpObY&&!=6D4ch9mB@@1La%a0>wm7LmyhD+$Ax-=b_UY<6DF4fiKQS*tY z1gODW?b|#cL%bJA0Y^CHmr`RYf^&XJB;&6Zq(33-{|p@5a1MsHG$`|hfG2P<$FH&v zZws#{tfE;e>sbG4z?5%DPa`vMwZa$5i_Z({jBP*mN2d0~bZR1>g%V7lOPZ1Ut6%218tKHHROu;S-#}4 zmePefvX5Lv^UWNW_(g=~8kV)hf<4D&2Wuy0NY(7m(xthHKvb} z@aq=Whs^l(tR7$0sHTVrcv`1K_-c0&n7FKRfrMsX-wR4;&ZLEv&z%lf zTwTlRmK3;r$p*bsHqx70<*@GmP>{3-7g|x=S56d=*KyD93`%%p;X5TN0ah+XS zE8eb!Zy+`k|>y!3hr7R{f}0Znvvc^lMx^PtCLH> zTB+lVu?SL+OSVmUBktRT`u#18DC9!O1i{GS-Ei6Sv7N;_rE+#qU{KpJm)dp(AdLn} zOO(`V7l-KWfr#b#!I2OUNRi4!6dsBHzm?>fJxB<2HwNSWH7yV1ToB$gQxi@9Jllin zt8Hg{hTL6@RY%Se`45C`TCgVaxG~vJXj`+Oz=X}O*innI^ZHt#cMmK$92h~LExy{4pv`dd z2zOv{d01KJl9_dWR;er%<237Ls)@9G4o2_OnZ5^$oo}M7EgDzdYLS*&0oJm2^|{83 zsEwY{1<#i3siACfu48el@T9|OqKDdlt;!pIAhoN;zdb=i1lbf3K-}zj8~Kq=tgg)2 z@vS$#Li?$5$QQd@Ob^bi(xD|R1t!0VMXU0AA*#Pz2 z<=N!L6DtIh@vSd^)nTJgWzhD18Wr%b#dfU9U7^z@Z zeS7&Kjh?>Vi%VSKEdWa2_Cb4o*1I{GbcxG!5PREF5Zd?4)#BP)TwO^Gk_Pocxljpv z_ux?Ba4&Mc-xwc2Tuf>S7m_;mNw|5^!Vp$;|mM7iB^6gAGf{tB*t>XDwK+fS5LeTKM6Po z25s6l!zc0kl3agd9bcKc5L=$PupH`c818lm=qpmP4 zmyUDjmiy)I&Zhfn+eSo9*U3?5@W7*g5x(!JXik*e82vPz^(V04{(P#YRKpDV!TnIG z4luHOW&^Cfp_&@p`$X2~x~X7UU^Jzc^(0S2m+q_}=u>-9da= z2!8R+k*FXw2-v|cJULmlC*Kca(H8nr9dKG!&npLc#B4b7m(82kLdecySFQt3H##sF z83Nf=Uma&&Mr%emXd>}*Wz&$##r53EWwB~;d1ld(X%6X@P^_<-3CLCq*2O)7*t8N2eiiNA*i*gKgk?F;sS;UdeOKjRI_@znjxaMt3F_E%igg z=}We^b;ajb?`_>ulVT=i@yF?bP0|tJ(qynuT7Rc9eo0Iv^9{}bl@BT&svO+ZZyL(z zLOyvYIMfxv{ci1k)!kN=dS{P z@iH3K4cLO(v$oSOvj1IXnFLq%1FlHPDUTDHREX$rZL)HHllhOu>$e=Og^MsmUMC! zNvIF8D=$lFD1NtY4yop#It)C^jIz-FM$)|AEO5_O6XZ` z`9Z_oo|lSzP&~u9hRIlDy_C!uT6PbO_sIY9@eGkj^?-tpB>GK%L}2h^?$re^RftP) z+zY`O7U;KL@OZCZXFM#0>+rdT`ZBKKNyjHazE%XOivebh$c+;7G9+#CvynTK3=efE z13&#p?m}f%5!9{P^wO6EnizK0IZwpiQQmmJT9SeEkB4%csamzUEO zOh8}2>YYEpMhLbwq8td*N@6O7$+H;HkG5U5=OA#GBT~8$GHI_S{cgR#!%Z2bDz13- zb$Gn0_*MFIz0>IIJawqfUO*<>HNf&`%HG`Jm$i(P?h!%p7PjcU5!al~%Q<_IUt6nia%l?UIbiNV zW7~9mGs`1B7&r2YwB~eECsU3o%CYN-mza&YFi`PV>cID1P95TRd3MmO7k zF@Q9~4wtbFjjdazHtSOZhp+RPLtA8RmTaM$T;+dgA?mXMr#ET!Taj`hWLy$jPj?B<(GAw$zStMK{{*ba zV9QI0vlBmwj1atM*sA2_D^kF35dg{V^D26H2pI(rwb0JA7X1d5*;cc6L2~oJ+?ind zX+E4!n|XpwDDAGQgfWuQS1!IgK1tdbAAzs$m7g|x%Xysrj<5v)A1q&;55KZ^?QRTb(`gth03ng!dmF+6ZuRb=JR}wS<8GD^LA)Q z02ty}y$JzJMZ4fC1iI4Gu0z+BTLZCo1f6T2E+$CIi}tR5V;LkvD!lk-^3JRNuX!yeskqVsagb4oR6wyISE5ZOiBgV=HIL zp3JnGX;n$enH4oIKL5)@V{OMUOtE+V=o|;49=zA`FN;{!KN@z$X`w|R9UuK3WZ;9r z>WHkIVb*Ldq{t5s^P=TEpF?f@quZOoF36Z1(LwQ^(EviyMWSL`xkOIU7?xlfc)_BJ zotP5$?ftA@p#2;UE_0S;t-JCb0df;EG_i0=L;K2ZEnw#j-@S>vE22c2#JFd0Rov#! zerFG;nv$2KUW^>B@NloiJ?tcKjI^9&E4DsaQ|;gI93%4-8bhr(7-xgu=%tZzh&Y=c z3pj{C3}(I>aU9&klff82&ja1_8U>SXjXn>a=lJR>BTEPM!7%)47fu3+)kF?GnQ1@t z>ivy-Kt8IxDDFdfLib8+z(kRdg>V)QU7t<(;h|w$on`m-B`ascXyRjXKuHfmcRp(2 zP{H$66g)(o?!Ib{-NrmzgiP!NFDheQ0E`ur*)16ZrN)rN*T_-k7ikusdL%i1xIpV3 zUBuc7znR;%7D7Co#D@mq`^PNmIsRVZNnU~|RvD+zf0`w7B70Rhc?=4?{7&t4IMaBQ zjk9mZR?z1hl3kTwRvtix@c|Ng&<~idzTx=$<1Zc*Xuf!oS|`Ckw)y&Yu)<%NbCS>e zd5VL;)|u!%?H}{7bmhQSD^38mTX5C`*%inN^xkoRUfJ0*S(t z@0KaW)aqkz-TcPd?f-;SFFO^BgMa@a;O!USR-OX@G7pqa!|%P(Ip487+x0|jf+>B# z7%;a+=5pH@Ui-ZjTEwk|IdttGR6qDr>v zK`}AI>Y#m8_+ofA=QaQszwuxbwZ1K>>PXK7yAdt`0N>-uvRk>igmgnr?UJ?@i02$* zf4Zvyzj)H$ng8f?cS85N6yjA@L4i=Y%zf?F!PX=Ih)XK6VPe+N_-DD)W}ztr?!aog zCYXY0+cD)=#afqgiMLTFqRkO2*LEJcp&UA@rYng>c9MTK`uyZ($9sc52T5-)A2HpJ z9XMv-UbhdoioC+52ThE>-5wK#3?XY}lt@rUvy~`B@+y=z_m-%sYdNDIPT7|pqEUQM z>~qq)vB*%-Ts3qlBP31W`I(mYvH1)E0*I-LjB#u3cy@EoBIak^&qig|(P>#s0?@kF z-2_1!k^t1WIpkB-ohCVo9%>9>TuOS-$%`x-r5;ZNP z6QEyaps?`|T_^eGNA{^}QZYx&cLzFjI$k5cDME4!0RgK6YtxHngvREtbF<;9EK}Ei zjn7Mbl)K|Fp`d|1z!^w=J(=lKIPBYF2hrh`@MR5HMZ&Fa&w6D0uHIfool8n3i( zywIYuSu4u+XZ(s?L~di=wt=zvF{yO0ZmBG1?gjv)RtI^`^c`pMQ1z8x(v!W{BC$4l z)KVKTfW*+tKiz#rytJ$@6$j#SCDA?`9IXObX6mXSDH90=)^4TzLis3s zApn5Mt1u>H-RXf@*GF=O`%~t%_07bA%Pyp-yI!~EEBG5qPJ|t6yBBb7{cZpfn6V3xe z7wsl74?PG%31F53V$bT*Jf?`c@XLsL>zQS1xXB(vfg@^uWxI0IeCJCS+DqYYxpLsZ z#YNEq!N>uefL1mXX!t}W2lNG(Vfe|O7(%HP-SZZ6q>Y7MxmzsJE$b3%kEu~ zSEf1Zy3infk@=lJmO$}cDNs>JX@+O{LCXWcw33|lR%K;p6TzRj_$XjPnHv>PChQ$? zvVF&6?lE!{RDP_{t3iz-$HB9p2u_%nxmGn53eOVx)pSUA_gP1qRi}EH^o&1ljMn4pzM<>(o}T|ZQI z>3(F(Pvf08J|Ghy-CUTKDIMRG-c^0DulbR=dj{0jkg_58_av=Po$BxYYTYhH0=;b9 z>|)mcqyoe$&cWO=H^io`5|xZ7hX-2upnf!%0{Ios3S`a2*`|X@?%tz-wat~fq9w;# z6##IWtLj%g!wZrL1a48boD>8uMLaz?ly!qSDiQn7-2+A7?`MdNjt_XkE5rl4R`l%? zN9&zh&es5XsCduvA}BJub!Carw!V-kZ+n=%Z*Um{vOozRdUD#V<^Oa0%g2A-0YcjC ztwF=hBSg$`(Hj0EAGB@h6apZvKN930%V_D?+9S6kOT(jVgR))(_;s{{7Zsp#y5OF& zo~x|?#c(R#l5xrC25(`?$LG8fq2Nn)sKNM-^~7iWxYa{Q5XB3==6qtmDnA2nWZC8R zh;@ZRU)R~TYA0=_noV=w4relM;ik%)sc}*!7^dT~PZL+Ckbm>#2_}&9!Ym z3WtCsmAG5SL29t>%Z;avBC{%87PR!UU*aZAEwLgEoTq8_-#6CBU14w~ur^Tt?*Wi< zzZQ;s@k<8UyoQ(tQlbQ)22SbAIb>a?(?KJ(_D?1rRMes)}W8nKm!}3QQ>aOka2Of7W+WeK5BxgbY?x)$a8=oq9^POTg-^0uR9F=d`;AYnH0lOg48!*fz)K)TJD64%7|n@Dr~QNT`i7s~wk zA7cj|8{eG3HNB743LCzV?%iyETxh3qfdc^`=lFpIJ*bpYY|p2kvCan+uaP1{er%Mt zxiYwFtBH%=5a7pl^a#R@TAY9}<_9$1 zuIl)>Jp3fN2N^S4dK0?UB*E352E_2DUA9wUnM;`%PFJ^ABkKr!3wCS`~sv~E6- z-_-RdRhdEFUL*fO1dk{$kW7Cn?2JBijno}t18y-w7~=23dV->gTX_9`& zPE?V2k>&64xu~le^3I}j83Pfk8I4;i*KX48khmLAV#g?__qy6D0Mde60(fcwH7gcz ziwg>&xbr#5d+p`jwg&zyKz3HaA*uHbA8kD`J(^1Qd$?ySugJE_#qfDArFXhxDWvFI zCn8w^$PR5|Jd~Os>(N6 z2AY=2&ze4*syvk^x!q>LXm82pl8Vu^bkJfiNX!-q0BjZU?{Cf2X*`U8o|2N|= zJmr8AkL8H?QyZBy8tD;M#)BZ$u#uSM&cEzND+t1NHQ~BtS=O(*munlIo*G#ywK9$~ zn;y{MY;I3vHZVUV9_*2NE1owU;GeW3KM6MHNMt_?oDIKlJYdi4@$^pMQ^j)UFk2j0 zkrUYVXYs6V9ZBSg;?cneC*NBX*55yA?{Jh8ee7|)a+FNWQ-^-6u;A?0a_N~s#cSdE z`~a0=jay!6{bp7Q&2Dhs_0d|5U0HVBmB@y}CM{b$c%%FRi{%QGFBQL&(2{ZnHx41` zgHCzk#uGAF|5Yq3v%PYa((9-vv6D4blxg9)By}OwMj$VK-KFZfEbm!y_qWc7Te5B@ zRm&ManI=vRaHyd;rELKZI%xCaXM4zzY(==hnZ~yEiL6F>?Teqn4|wtI9kslp*rxc9 zTcoq^h;>x)%=7NBd0X=jZ6cYz^v;o3S>4c)pO(lAaM4r#YLtP^y2*H`k;j zE_UxbIxb`#*j+i3?!9&XYY?+5D{v)iDel{pzI)l@^$`XUK?jpECTK0@NSO}r8W%D- zu7(i0bvz9RuZ_Wzqud%%&&Eg{-vHNjl}d0&iED2mHfKiqR@E<$A6~TS+;?wEhNg-5 zQ3qd4(ONC>H4W0t_ zSF(i5zjjbrfYVHOppa{Ack<*>irc0$;!$ykOvW@bKCTI&DA9?poO! zWd3#iPeH6-bjLmJ49-60>uI_INoY5185vSPDKqghg>c%nZvFdO%vi7-34Unx>MsX% z|LUGveK7r+iH~v!Bm>q2_pSjoJONgvW#Iz_4n(|&A(fq7qIuk7E<2MOShG_nfiPZ z>$MoBNHiF;cSsz|QvLd5j}-vo%$5j;I6wW%Sati2kAoLlf(j*n;)tL5^uj(|H=uXd zyAvAunGa(l;- zNDG$lDP+{VdT3%25r*@OE&mY(VH+628s`g;eb$-Yb8r3qxD4BM+b5+$>0%o0Jgd%D zr;gfSXg-n)^j`p~s0qE1g{G7D^KH`$Gw(9r6>;?Of8!}B(vWKDuA$}$? zJ4?JAgB<pk`_{q zn)o1eFrTlSgr|1ZKVIHN{j9Ju26;b0m)RwF@c#bKIOt7x{j!_-60di>f|WHFbB05$ z%ll%CB|R^S__&w1dc_#|$sJA^v0@OJbsxv=S9gY;@l}F&v94jxT!j$$ZTm`5HN?ET zCJU3M^R;M}q20)1hr*fSXR_9fcw>4OkI^eSRN7~kap#~aKaI9_DOa|7_i6tw@Ta|m znfM`-yhzCT`^24dD8=y1Udk6aCen0fE!^mrZ~fQ#S6)rrvim>=yooZi>-Yac@x$ob z8+tlI)!S-{VU)k+PM{5w$3oiL`<*`{0U@yhlVnxMHaRBQZM1$-K5ntVN`I#IM&!7v~tO24qbLm zbRSb|MQSo$A@qTXFTw@VJJ}+2!(l17-ipdKwvoqKrNkR6-!7WvFn)Pe+uolw9FDCWV-s)h|-td9gld3F*Tj+DX|0 zx@=@Uxh4&|!-dIOa#yr;*!=jK@5#N;zZZ%dk|~-!YQR`ADR)Cwms0Pfp6ItTWy*&E zV#LjMCI0+uTRkP1b0tH3C9iXGdI%2L{@m@DxlD{O`r*@I-{xQ>NM#tji}KVe)_r16 zO!}CoOZjG2IrD*7gw5A1s?EtBCc&}A!|J#Qd+|&vy@02)GFj58y+SVUJY$VF8{DiK zJ@(E#_qm#C{Kj7$U!vdSb(F7TQPDUW@%1m}i|hFdrLf!+3%OpDPOxhcZT$q+5aLh) z9->;h#6sCIWSH3$95SIvQPzTgyj?SQ(MOR+SBr$RM7vA!<`qu;Kwz-y%rJbMv5{6L#~W^r?u~ zoF$ns9jBzUOYNCbY)=lbEjb;*!5E1JSa@@=gdO|mS9pfXNWlZ6MCjg|)F!X{vbt-lLj+j~K1xT2hc?AH@&`qsf-l zuwt^rumILDGD;{GgnSQBYg{+Uu_#oEnabLhV{ye+brELuSWT-(ZCCL23N48Kr>X1< z!N{xDJqD7;xc^@`!b=;NyKz?2uF2T>Wr@9bo;J(#6i3#K%T*g1fhUjf?66?7-YPH! zK<8}&CF80^){J8GBF*qbnn5Tq3%}k2#~7GTF+;r*@KvnyXp!RjZ=9H)q7$+VgE_pQ5guQl+wkWT$Q#|Ya0^q~h~ zv=K9R9x*4|5G{I%*Dw6ayyqyvjEs7}S zDLE^I&Yrh_;G zv`Nv4R9{k-itztWytq`!5t5N!8L4HmG1>|-glYQlX^H`{doS+QND}8-8E&1sw;>=i zIkiGnGIwyr_k+xCHYM0YHDju1CopkQF~XCXjKEzp1jW*?CuL!u{SBc19r!MKni=S< zxT`2Z`C4IAAMfvJ$K@d_mdDtqMlKs*8H8hL83=vTjqOt2UlQN{%rlk3&6TU^cKJZl zX8Cbp54^8vdin|RRR;JtoUcLyF<@sJHY0yz&4~;2IY{DzY~Siy`6tc-_O=Vm(f}L5 zO-U3V$gvFP9f$Il@@@>tCjUxWjYuicGohl@-`>FQ=UBqp+oOR${LqqMFXNDjes5uH zlAMp|7V&6SopoBp6ETqh#h%&T1lF#bB~4HI9)+jGsQnF}{+*IfquA40%h_PHmA6ze z*O{T2kL!zS@sC-#>}2_-q;xh#7o}f($y_v4`wH7EpyJ=VrM|5wvvzmCA`~Gufy=(! zv~cFRfraT1CG6p_6BkkGBoH4dE@1A0O8@(L@CO(%oW{MGEE%SllKj~B=r!Vr=6RNN zG1lPM$a!YSAtx1EMiN;X_tN#ENSx7GP2bc7gnZwx)t8kzu$SJ1tz0@P*Ku}q&K*Od4@=)1{=cK)hSY(dguk#J$> z-eU|M6;1T9Qr=ISMmJ+tGH@m>)!dE4WayiN@RtV>a7DSYcv?vQRS&=lyrErB~ z*brA?F`-~x^`F=fI1?X)KRNnJ^K)m4*}+%y)er;g8y+1!BV#*4wJV|PEX6cZf=Y|! zO4(*!Qu!$icF9(~z1D3UZi}7hC1cdLs$@eU3OXHHxE|JlTwRJD_a*C}H=EP72b4_p zY-hwQ<>>|++30@8x2BS3_S$~&80h)Tr=LmHPMnABSrB}Xq3AoMe+(2j2bC5BEkEw( z)!kw(8oPq?`F%E6MCyqK^vzu0?@F?djLEgM=ujV#k!8#W;5X{Im7H8Kbm-HCMvQKyH-J^$u>0w(h?I$kg9W{X$OZdk7 zJ5f-bR{_ni<`=uK3`{b4`*LgMs{TF6XtVgY=KB`r1I?HjMVN)_ionO3xiOjAbiBER zy*zOdn~URgMk&i@P`y#aPaGU>E{J>nc1i}-Y7}IZChz=4a?KUxQ8IGTSHxFg^I}$) zkm=7eGGpid|~xTy46ywjn!*zFV`!O=>Yx>{#~ddW5avdfCv3 z#c=p)`FQE2RXYu=;!KkO869#qB@?M?Esx21MYLS${L@uckQ_|-S9p-Q0LvAzCuNu4 zl}{ST9*BpB1Pm%zKgzF1*PE=ok3b~Qv-9byp+Tq{`(p|(B+T+U{*7+{*AS9I8>b&R z$q(xk^XFUj3n{<4%cqX<3Y}*N&}Mu1{y$kudYZti`bp;#_()nxrpo|Q!P!zHa9xOE z?zY{mO?gT~2Wd`aqmaAN#1Y*d4VgUdc#I_%?8{R|CF>%K42@1+`S88sa#1$q-rF@W zIAhKBpVO5J#Uw3HBxdk>o%<*x79Jw??lY^_6&;#a{U{GJcv z#>{qt+8y#f8WMQPNk{VY{24uDwmKJqa++E3#4nH*mWDv!@Mv|&4lU;fFrgXsz|s{h1IK{?q)kJ6wwg~dzK z%>I-Q^Q5xZmpLBN;NH}}c9}=-jYEDVTFaB1h`wTD8)kCymanblemur(=QB+9I}8Qo zDiyrk$WkO|Q~XA)!77f<7OM3xF|u+89j^ME-J>HP#}TDDj`^m=&GBI4^Wta+m|ccPBR;;oihiTQG%bCPd<6dZKcYpW}Vjut(>!o7|XZz|Kh|jaM;OrZ6n&W4!II2S< znwqbo2nIg7QPCadHhI2fG~~4+-Z;F?0*z+IuOv-67%1{TvA6Yn9?k6KKG=Z2NH1LN z+z0l3Rkr+hf0`1c?*1h2bnUe~T_?2IW7X{Vrw?G=cMVf%nOUh${=Vvcd-id)rn3|RR8i>ye8$)$|a6F@$|vE)5ths3*n zEUl*lDq^`97EhkEJ{q*%Mg1wvgJ6H&MwF`X+>6`dnCehO&4~PMe^P-a_IQzyd;T0D zUYq-I0Iz4N0CcUqEl*w7ptz{G>9%cKlI`l}eI1s!$eZXZRtOKHHwC66ubr0mXcKN! z;^`Dm9x4&6{w{#&T|V^4+%K~k< zU{xsfv(@c`=a;%hK6>iI$No6hoJe2HqzS;Jwc#z_q;#}MUx}rLM$WhLL+wpOaf%~4 zq2#zVQcm?8s^BVhE0w7pt7c5;7q?EJZ9|Qp92hm>%wL{l|B;!6%>zyq9V5A$+yTc3 z$%h!5f3fHgxiQJ9Xgc{tr);~~HVx}3tj!jr2?lBroUC}TD7#NT_{a#*6J|31T zM;&8k=XoI6sdVidgT5v2@5Vy!*SZw3UTS!ECN{BYevJLvJr3ubEIH^&`Ar-DMCJ!` z_wY$<6uQalpBGIAWMQw;DTZNI=!t-`7wXf}bT#No1M@fg=Erev+Do`Mr~U^tnUss` z7a%vVo_n4$%LW0lYJmL%m{3Yl-HnjINpHROqc9y9_yC~R ziRONbieFP1cLt{kNW$y@s`WkLDvIi~^)O8*1wq@+5;f%6W8acWL-2W9O8;P8$A?#j z_c%9F(%?iW((sGdT|73LPx-@`Hh*kHgoiNi$g$Fh-Te28m+5`{MP_vEA=LI4DYnfZ z6B1CCQ1@Q-A5m)@77XsHaw8S9WYwfV-k*lge@c~|(?3zL1BZZaD3xm(JF=c-kWQiO z$%@RqJBEj8@s%o8S)uAkGp`Di4r(n-tvj%58%#G5@fR_^w@?2IK@T*sas}CG_I0oL zX|fgi`$RP~cRB86E&Pf9`o;=yW#?|Omp>P|+M&Bfv`rI@tcLRJTyLxeHN?hR;9h6H$={4Us!;o1dnc4BXTgf=7bFt<3M<=* zSfZ~TX{Ps{3_~y?5w4B>;CR|6p0w$(e@zr-+?pi z^9;^t`eY;x$lK}_wQ&lY2~gT6VJGWu$9y*qyS`|pkge2Kv1zpWAtJCx%O$HcPS59? zNSn3#%)t*&Aug$hjvU>jqzBJtR+h`Xd`PN>$~6fls$k77ap;c2w$pWRA^WI42ta(S zVPd&-!y~o()nqGp-#Mo^qXzA?SLc3FSmhfKk}v!{th_t7NJqTUbjF@O2>B6C-kF2e zpC^1exO#u36f`^hO{O-q=wsvNav(3!f4YeBf{03}|iy#&%U-Iuo1#U;5c} zCe!%$_-B_V<+n&aPk)^?V4Vz746xzvqZO|!Z8s~{8Fr0u5~ zaUShQ%lo>4lLcXa33kKi1!CO|Y^iYUW~hUTKA04cw`1=HO|}iF>Ju z9+3&sumi`f5VZH^=FmTR zc`u$&JBu;)`VED@rZGJXjuU{CsH|iInxF%RSfgte*StWvzkavFqFTvCweUCDaoNb$ zRWi^Cb}%kx18zNsU9b06G-03K{WvgA-x@tUq@kFm3V3giE9f+0fSg%2FCvs|fd zCHFFZ-N~Wsk@cRPt;Bw$a(2Hgq6_0! zEpI4f+13Q(O!UrYZcrG$K&OK8wl3V31^l5)IW+>XvC7zq>HxahP@%1;#<)>9OAX7>xB*NLM zp{_jb-{8=MKCz_~Lx_tYxm50xVDc+{*UHpW_XDTvArrS{#vp$$A-k`v-jQ0z+$t_M zc2=AGnO8skt+=GUomtrfu5RZ^v4K(7^Y*>uuW`nqt?MIwR*v2Mj;t0q5t;?3Kb?Fa z`avN_aX)&>06UdRM|G|@SItHjtS_Yed(kJmO!>IU!>fpXmG{@$KeQYe*&p-{`kAQi zxPMM>{Usooy|w8uS7)s)$dwDGjaq)-Jp6lqD+fS#=&ioAwEnUmyfv^RDDwOfuO=Pb${MctD zO|+~WhR@0}#F!T^R?y`{@Wdh>E0n7eWrL2ZZvk~BHL!m0d4F$AFbI2Fsb}3nM>=ZU zPwxSjm^_~2d?#65XE~1=Kkl100<6k@S}Y+;fg0q_9`D%I+d}^&fO&5dji;Z zQhckw4w3KhBM0PqNgEZDrt8Q&LEu3vB|ycx=>Ga2L2mAxjM>@DLV?Lo;L%Njat*7e z($x6}FCA@-s6Sn?9=La0Et!F~vO+Y^ylZ+101cX9SJ5{1C?Co=Us7|`&5(4eY}*?x zNzvmFpilbC4txHGiTZyd9giW532oIvwsr3>-3~ePCI9jxd7KRE2zAkdx9p`gPK|W{ zBoN0eH55f`x>A&BQ+?m^B14OCCv7~B&KVQ)>oO09IC$ew$Z)R;Fr}Fnm46 z=(c@?usnxVCV^cCe6;`WnZoS}0I=JuZ0XEK2{9OOss6SlNOUfsSE>nH`FN&G|Ex4) zUb4DP)Y3Gevw7&X`b{&A((J=t9H`{E(!veGU#vjJABijV9}yt z5upH}7(R}Gp-R4g;}NFA(h;blV50#rwU9NlH^^PVvNsx9u4EMKMs9z;OPY@H{D^zC zZ~P)0La6d-25Xf+glg_L9*NTdZ-W&(6M0Wr^{-@M}r-O8${%A0>JKDcOo z;MF~&C9QB|MdvE+)x&hdu?N0sjL-AviarRTeQD4R6{6!uLxw+dAcV&huY5R%+O(L^ znUv#&W@M?eYv1Vi&-@JO8aj|bpKd@Zx7E=VwIl4`x@Ah{**U^uId(DUl#l^==h>$Y zR%DI;C|cnvS%jQY^>%RWS%pSymXHGjoD`tZtj0vJMikE*`jNxaE*~XkJKct)wOe97IE}Yr?N;mxU zks{}hsRu#V5oKRE0*n%bAiCu`QR9zhy4e|@GXeeVgByRAlcy>CoaswKnFz3d6yeJ- zK4kejm^&RPV}Q7rl*18K%cnF|oAyv2(X)Groa2yv=#zSpqiQadoLohOBsa4}qe1sCmCocbL(yD^*t z)d#8EUT^LONMo(%k)Ve29ZG%rW3N!8)m$R^%?*j+qX0K@biGXAJPkx4vAseza=a(k zmrVBUhiH>O2qtvZa_=2!YR@*0=6@c1UL>pUCl|asl?Hz+mYNJTa>s4zJh}61>vzqj z6B~n0EW)Dg*NPu|oN3o8e!6#F5UMKo7+~j4+I&>NH}mH-n?uiM3(0{B4}0jiNE0_D zE8`&DiV3?vpkOZZgzemkEdy%AGaJ;ETpq!bN`z>W-QN-!@~)SfxUTJ|m&}Sg zuhlRZ31}{-dPWH~I&XnE4)lU_*?s&z-e9Ar%8jw#IrzF!>U z%*zP9+Q5EOUIVPvtw~dbM@RcCU8`N+`E&F=7Q#P-aRGYO(8CutbGC=q0sw z0%}{Nr?_GMLD7V}kal)cc&`8&m`UK=Be(HuE&`LC!6w2>oXvfEfZwPSN>;96PXFmF zzSYZ1BA+DLW9wWHe21K;)f3-*G((yn2bEfWMU0U@PPKgs&DfJVb`gM;MMKUrLky1r zk68S@4&F_hPgqKD9Q%_2Tm^-^=GUKMCz*MO23E&Yb$D734fP2UoDOSGk7q`YLyX1u z=%}4eZm@Y(zJb~J-Au=v4ha2x__OaLKFO`kbqg0#Qpn88%+squmePm4iiB0d_C1mn z4SK@CW7YB^ct9phS;E>>u7_RVAo5OF(HnQK?3LyTn?B0RgJ|Eq=Py8JF94NU9Xo+2 zJ?oFxbn`5_Ks_O9TVksbl@to_sEJE`>3#G&Q{p+)0-QDhf~|Tg}a2YU!*Tw zoQ;`h8#n2>40+HQ;}D+2AWp-SV6WC{R+a@N^@~$|)TeJAT>QW8qU3M*u69?p_--i1 z`My{TMrYXiO5crS>o4C!38qxuH6l~JX|J^2>=T~ddvxiUaL_@QjA7Dj3uWZi3$wCF zRl1mSq?VyE>J!~a;>$deO;Rq-?GGgP>EqLQ4dqW6V7Gg=P}Sy*nY_pK$%uRS2Lh(T zxdB^e7CzyHuH;7B|4@1Nk{c1R{DQYNayf?-HaBjUi;#~Oig9hvITK@h z!gPv>Q_mLN3P^FNtOaaDaL1r`3dz&r6ZUXl@Gin}ZH)%-XeQg9DsL(2Yllc15&2<0 zcac+)sabbs*(e~VMw#l9c23d{)Ti>tyovAfd0oiwRlr&3DriMy=$2%EEIq3i2ikLd z78cd6kwaFCA|BivU-|6lh1)SEt?fkbsyJQMgBRn8u@&(@K?59BHj|uc9`R^!lP{J+?=$K9gA<5mYma@KC!vZOGaerR zT$!AFvy&X+(v?B-dNNtB^k-_D*QqsNv99fSHT)j6X{|XD7jsNU*5y{6L}`wdMLE#v zWkK$!biKQkdn6Un7jpyStAUo#qyi9Jm5J;%D9Xvdw&~T~irdGoCAw=rC*sI+Ho0?pSKD7lXYJi%QpZlWqv2}L?r9sBVI zgU_Ea{~}P)LG+c>B6kLO%8Q}j@!wTsN2U&ajdVLT9;AsMdYUmVMKEFLCaxgEWhl{S z(AHB_f7kp@$y?L&gnImv^)n_p69H3SttzlwnKklv(hoD>`7r0T;e*Jpg^kFqCs%h(x-5QmK7!gd`|uDk9oAXela`?UR-@#L>?tGg>E8 z(HZ;xCd31GohS0}Q?IP2XqWhhsUE)6az5pYywnJBV0wL9vhvDr;M8S+b@`+fdMJ{{ z?$yuF)bHQlb&mwr*R3g?C;(RD+OUWE%6L@0Lv=|y{~$_IV0zM3Q2A6g${>x2ZBwo- zO+KBce&6jfj{{$t6$YKlMUj$RF@_MHP#}}7nrKV~=vrG!cTBaPq{ssHJhVR~nm6Vs z#M~|lh1uvutbM(VvHfOrKxoJ~@#yjWG*&&#RiqSr62#Ofa}87{6qqiST6nPABl5pd zFk!a%G1(Z1@HSDjq|S+t-Vl_vSlj2WArM3<=ZoVfi*?VvHa+=>d*;VC6HY*FsD}iy z-?!jH7H&MM_XkF2GoB&uJ`(XYp$U|j?~})mIr_K4Azi+(%D3Ih#-e0Z2d2$CSP2?o zdZ@uUQnzY_8#FF?TFr^dIpa;A;Fr26d%CL$sq6#iP}Rt@j0uZMW%%6-ZK05Eb4cF1 zQuZn`cL*5Q01$EZdambpPH^{0hkn4JfK~t;paZHMCzcnBvgxS#ZM^#90u)d68D+51 z*1_rp>-`5=V7#UU+%qvUe^P$9M<^P>3>}PySiQdx1d`ZMwmrW7CGyx(`EaVPvS5zH z7EdberR5`5zfI{ZWSZ`j6l&f)$YF^B6s%-Y>SR+6oK!Ycs~c|GF=$`LDc(rByV@w& zBWk&R34MB*dug2te5f8Kg*zdqKu&|V%V*wa3W>+zmuC0Cph1K9i)UhaNs!bhV?RGbBp|5av z@)q3u3@rI7b-Vn82pQQG83|Z+w;gvLi&9~R+KcufG8I7Ua28EW?s zXy@{6SoWrwuP+nyRJT!}_xW}0yh)N!<2M&)Tt${JUgZge{g*=n51(gLeF~3yT%p;G zZcvt-_7}cm?R#T)32t&%W7N-&66B-sh^1E&TB<0);9rk`h5vyIKadJz$%S`LBB5N0 zhAorlW;?P5UXe49Qtl{a@7BIfFnF)3@NWG}E+yh5M?3u|HaA){-cy z1SYg;b`-6B9Dzq*ogSfB0wDX8)3_>so=OM9A}PNrN5zDEH%dL@2VA;pz_~ak-4UXj z7AZVneALe*#K}PufAXZhhuSnZO{Qgu<)vZUx58CggR_nP;CYw%JF|&5q^Y-r-h7%5 zxLlj2WjlT}?rO^YJD?X26$6I14?HLOIfIk10XNfaD$VlPJ#{?%tew7fGb{c2+0Nfx zgB@d(+?x|y_^C679n*v%Y6I*ST||5QpVYIY8rN3XeeEwG%*#I!{%+jxhhpt~rApf? zyHkpdM&6D=6L8Bl{{NGv2<` zQbV9b3A%ABlKuJz4}5(d_>NjUhI;MXF(|02@xtbkpAL43@6@dQZ9eg2m@6D86XS{` zpDQl=YQOF~92H3Q)i-#II*mj+7F_%qEoqTYS5z0^oAeUo=LN|u?1qvbd~WGvdjgdL zS=~9@ICGh}iX7>9Wf4^baCv1A|GAU{9Hc6XLo&OEi#B6mrC<@AP&jK6m>SfZlQw^Z zi-!LW8|Vpe0yBe&=WKVeZ(U{|Qfv0k+I99jb~UpZRAAOAtd7T`jv+d`M zDEtdkqpk;`U>+(K!ls6(BY?(44fD^&fx8F+K@l3YB`enUsBB`b#3Nz7mBj?MC?Bm$ z)q3{UW1{(m613P#T{NL-muQ{gW4-?&uO%OG6EGW7mX$ z%7GyezzpFBcdzEJ(|p+7~LlOJn%R*sK`Q>-b3bNz6 zGXRLTzbfl=P5QcYfGAFeVw^qksHLpbEM#FhqC1jP`PUs%Lg%Q1;bq1!39dpaDu+El z=QA7d<0TUUN&UeeBVR3u7*gz8k7#?xjj>yV0GM3N>Gw_>h=DRGCRQjCYiq}f)sFGI zvk%^}WymRK%$_ZZ^jDDpQEa)Cb8cjLEfB+IiW}6J5z+Xo8R;5;!GUcdcD=*))~P_N z{VRA9l!zA`sM-pbA<%L5!AFnH4sgIfdL`#vEI_dc3Rj`X*=K1Whj)N;1GnghF`00za271B} z7@@?O*N>TZ-L#+B8Ba6>o-srIlyL$W>!`X9>8JgiwxvjDqe&jB*y>x1N?Ktuaaidn z_16o<{M+>w3|b&p;)ihCqUdmJRYh>|K_cp@ChT|U^3)w@BTvoPH3#GaO^^uml7$9! zV-C>eNIU=ib{f@^zb6EU%S@d+f9iFfHUtBUpfv}Ha6VK|iP%u*UV!8SR6^;H8iDv? zypM=>bZ&=k0r~?oghwZ8qi=9x!pyI2ZY-?DsS4E=1!U5$kLCb>eEasm@pDpw_nkOn zM?30&#k>I8H#u8q{Z$M%os;6*sCgK(loK+wZ`NKQNG{ACw3sk2FW)-E3xe{Ao|{!Eu^WzV`SgUfJ*jIW0~j+yur&W?(foCRJY1<yUADPYRQ(Sx0dNsjI}O0(x-mP(#~rG~ zgP1~+J3pvvd-9o$YjPHXp5&gbvq=5ld``C;^LrLs+hlySvb8_I+*ZoTSF`xwsFU54 zf0dAvJTt>~LAzq8^#rFp{qZBl z^tW%5cwsR=QkM)zz{2Gxh~q1?$q*z-k-;iZ&rPSw|Y4^xD}3V151{yNaIj2d}e z^5-Lg7|Aoy8H<-gA%O362Lsc;(xnZ&xHhQb5rI`=z_KH6`F(sb|FK0}fc!q2UXlPA z^f9c=sn=&>#o(De!{h)(oai?YZ@C@xOJWv&JNW}9{^tjc8HnpbTWa*TPowSTFy- z|C$K2*-|n|Q;8_J!B)ifja^tUCiu4F<|J+sM-eUih&-R}&BL>rAJ`DA9rQqgQNcvv zgD9X?3Hv?@ctE?I6xOJ%U~T|M8v?;c9}U4H>=+4v-ruX2qj&B{v#x2l>BRSN(8Bw$ z#a#XdATpz|9>R4!0@eI-4C{D@ZzC+V?)+X$bf|d>&9Q2T3^7kfG}d^}d*t(6FXhzW7ZV>ObZH3bMylC6(uKY%5d?leTfHa* zkU;>%2?iVq+LZ5#WQK0!r|u5Q01ZH8XpWZ!^m@5vfP?LmFWoQO%_wmb<> z`{4}O{Drn{8ZEgS;!&?rS(AhT9N|k5Y)b#=9?c!4ZHG>q3@WZxEPDx;u<2IVhaJ&q zyk&DOQ{!F^AYGh7+jy=?M)-awz3maZ9&-ig4x0Tn%-^i8P$F`cjqe|=whC-8e>JFH z57wkTlD=9-|D1k?Bo5C0l|9HmJ!pcRBrtp#RwSl+ACTGHCPk6+=pRVlSJ2;8n$#({ z4-e+%+W6>M{S?t82q`vFpS;ud=&p5sICLx*BCPx=xX0wP2HI4YoQmfHqRZ7jy7EcE zl?>H}gv@x->K&pH3$z_Hs~E~t&G9B^mmVcIC2EL&)L(9bIx{n5*>4o|*fT;{H1ZJO%-1=eg=m)L{ob^1uPR23rOMYnqTG{{g%fW=oS zNB_f06=;XERCCo#%?iT%vB(KaL_ zgM$0k`&`y>RJEgDr05&PT+dIudNPx z94Oe98r;`Y&w)9A0s0C(pOps?Jfwts#TG{OlU|!`9aq3j-KFnJlZ<#*R~_ukv432r zK7Y>&M*G_W=3v?JlfOsxC3hm9-{#HA?`|6k=kAsyU!Ow)dVac!O)g4JV!DZIxnvF8 z-WDkNZ`6kwgwCpl&miZ_3*rVy3i;?DGEi6=mPeO=Pa45~1qcRnutA#C1#?2ilSjT_ zBK^=qY>zLVJw3_phigyZp!k8+ykKoL)Yf;7R!K*Vhb>2ikNtUMw^8nE!&tYT9)?8~ zU^*g_EzHhYM#tPw`s^c4$O)YrEn zzV~zb4H6pCGl~^&%tsRx)mfs3xfJR`~-XzXEOIXMXR;N zU-wOk=IZ6t0a;tb^s8D~8Y2>yH9 zp=h?~Q;m%l$K2{HwGrljnow$A{Sag|`vN>qnCFbZ>dLx{1gm^_#?lr`RY={%1o#wxRuC|MyT@>?)dVDW z!}`0Y9!iYmvvL0TiJW5;gG){flW~S}W4Lrt#U=rdz%a-S%TLOH}cx1J|z||A8x%a9iXGL?v;gi8@mrnNLZom)X>%AfECXg z5+-E?#&|GY?YBbNdar@dx|Kzw>#=vFa#TqbiXM)pKD8`Gz^VM5(mlvIsZ_g^wUdh( zco8oM0Qg*>6TuD!7%bpujb7}Fl|K%VYPAn;q~?nh891^e6PC8OwJm!K@428thx(97@uOZ!D*a#V>RrTMT}1^DEIIj`}?lB=a@>yDzovj9c&jV75|L0|@C zGFrs198)*my7hQs@IKQlRFSg zMbv$?U{S3rA+cWgZpub+(lx)Non)`B^c;nxSmLuF@-?|+3 zh37FzxD+ceauOyCzKq1vLM)eUfu4jY>5%V2K;QrJT+wj+wWqaC(&!TpP$F#Lz*I77 zh$aa1bPE&-idqx+talGtr`6Az+Li;LfBDM?-*&LLagJ78c!(X?4famMo~VZ@EH+rf zPnl{w{&&fW`MHa?;3fw)bAjyDj}bheE7g!=zhdTll)fVslcz8h)n|VEBH|uJIvsxx zoQuskJI72Rq2N4-tuEW|8bI>-k`~lk-9UA9t8+%VvYrP05RU>y`hQxZ=v70!X{>~qHz2qhLeXeodXI$r;>&-DB zLptMdGXa2GG)@R;%!Fnjz`N4~siVr|kN(6!{VLd9f6fST8negCSeJx?97gwgGbG*+ zT9!d!J*QS_4|9EFG~G;tT=m~w*s0mWOOcaPz^6(5*X8j}>?0)1>tt0O1HY8l0|p5u za>Rq#jjDb$s+Xo>#SM4!lr`AApzO%?tpYL_S{w(%RRm|jc~9{A8|`~nO>H6IvCG$V z2oOT|*Vh!3S!q~+`B@pNmGifRWS1}(C}g!qUZ8bsYfp&FR1$|mdss7gX3q7|mVW5%(fS_f)>d$9w1C z+=r6`IR7uuuA2{h1w}cGV4SA3QVQSSxH*njJ@MgEM(OE{>;)wldSa|xgJb$Olkk-t z@apEZ$)GbL00+2BK-&ZQ*HsHN07Bw%s_`a>2ZB!@RMX$-V&$^SsaMRr?hRgQ0$Lx2 z_g3BT4_R85m@PHcic}_Nhg2F;9;unDmz0vM*buJIfLQQ}K{khSdhI`Q3jV$S8FO?xcAiI%kEFtwb5F4~aXvHTo&8r^47FP0itJa;%*l670&dl^EDF z0UR}A(&P3#geR~?)qfEf084U=A@2CS;^mEE^Pp$SNhF25J_B+bZg|D*%ZTpsR&jwC z?MeR`8hzTt@Ua9+ERbiq=U?n%06xU(&%neE2c~o?D~0+9-6zSog}B%bQIj*7=)EMq zxSs}|wLKRDo+^)7h7xuJX5Gd&s$8S646YJKb++oyCSRcajJ1IDazRjDG*>L%s*&W) z=4nkva%sgbwjWKBt+PJG_+z$bft(^*ZIN!3EmBAy8qXKJ=Ce zRZZy-7cA2g29+C#^_G`bwdu<){2Bg&7tND7+S@Sit$eLHV+OTT%z$OaaD4=y5P&@X z=fR)MHuG-0zr8WnVbc9LY{v$8^S=8#{MkkBfsVW8tLV;94S7y5v6xP+B8}PzYGB{} z+SOk>L$IE{_=$X$CpAgnEF+gEfZ99AnczZENSWurO>x8Dd)P-C_3W44&FuS~&mMSr z5degZii&eIdP1UUI3x=|W5V(g|+qIBi?7Ke{PZ?lUu~jtYN#=gn)j;?~Q3{Elm2eDDbKub5-Ty2U(qva9PH zuE%KBsRBs3!$~o@?if@hb4J1{EbBJ?{kokgoDvIi=MnHGw0ju7H+RPZ7NL3R_L0JV zLvg?w^jS(;exxp7b>pd{56L>t`%I}Rkrt91_Dfn1#xhfHPIbvJki$|@6a zxGH0)^LL_g(a~rff;cDSh598n$XBlgjes_vl(|%U0BG{s!QXlqKh+n1Dc89SCc064 zgn62t5wQN#N0&Y~!mX#nx=YDAmt#5^8=wROjW=QXOSZVyS@DgzpIN*|x67#)I~V}> z7WO_gCIy;ZzL%foPqfTbym$8k-8fi%6^2|Hz%ihdwn5#;+co1RCDN#Jzt4DSPr6f} zPOyCT-G6j?Du|T3+q~|-&tpUIppTzp}pC2s2$idt_k~l z+^c_*!1G|^yQ-hDg{2KGSbQl$japUa!0zwYp4NKb{0Q-3C;hO$-}4hd@YaFTFo+~m zm-8pnveSm++R*bs=TyN+6YiJsJBs`=&-G3us;xAW@renVNR(}}!7ta@?A1DB`-qcr zo5xA;Y^q`Pxxi~!>s+WwJ+CwhR;MO?RqHxLN-AeYE@;*ggj9BVNB>Q#Kg?X0vh}LF zUxG@vNe#?k1oJTev|G8wSqSKr4E0{k<*>eTcRq$H9k|#V0B>|Q-h=|k2ba$L_OwDQ z@cFc^Wu;NoFUbKC4V{y1S3%)O9Harh+9i_jk1T%i4!VGv$or0Sa0Q3cBar0xJlR(l zGngRVh}s!{>7zuuB$$&c+FG`p;lTT7YlRGVLzO*%xlVvz=b1M=zDnKntTJX2?V?vs zFL0Z1E<%8`jwBj4Z*#LH$^LeqtfIKru2Y0 z5gN&ICkt3v`2@=!U2M|zf_}yzX8EF872Q-hgmxRj9n6*%8WUq;B^fn()WlE|qr53-j7XBJ5 z+iwJyWOl8oEjpdyp!^IAsgxTrfEpy!1|FEF?WasUA+4=6_nhB#B#>vYt=KK9o!04i zQupI>g0~kOd93`yvDuBMMJ)2FLZMq*G#=jG!uR@a&P-dsd&$?`!g>@B0?Y)!fDjBU@5;U-4N{!#WUmPW#kTNi2XJ`n3j92MYW+Y)XSv?`#H$xxiI4 z3~f+k2#l$iVqbK$&f@(HV>#smvwT^xBD0=>=Xenp>w;P4#`tghIYw20H zTV8#rA*|g`+tkkq9&Vc~o9M@Dceyx>4>>0m5~~XB?a}>E;?4uio)zO(>n9bH*5zW` zSZ=h`ngXaKzBu@?9or(^$6(6|}MqhJjx*bJ;Ujh=v+5;51;kV{)n*CmpikWhy59Pd+y4{Cvhp)=jA1Hvv z#y-0DObM0o4VY_K@~1splo+x{2?+0-M-&pxbq9SeKPkSGBC=?9p6xDGCt)V(^|423 zb#~mvdqRsjV-zmwnTOdvb!j4_4NAn*>@C5oo+{V63h*vbHZ+aBK}nZM0xh>D{qCs# zPJ^f!2gXp!6e7#~d*(f}lLZf)gVFne*NJFR7rAA0(Y6q1SMUJ=aLVoF2;a@ut@ap~qHC-83r(vweAoArOpYJX z%STxD{Jc`^WlPV{6Q&~&G)eNbQ}8!6rHFNzy2SY%EC@Y{*`5dqq)^{#Q9Utb9SFWf zRWOqCAJ@;lx9EuV7S=hrZUycB4Ed?wnEt~vL1ZD8M`&dwLD%|g)gBJ9w90J(&%!7g zaaW{08BhA+6z`Oe#tdho)3a^b&B*7wex~acH((OKuBDc&8PXPXjZO%H5z4E;)1EEt zvnaF&TY%PTNkit67~N->of@TPz{U7?HRLJwOq?#Le;8Dpc8Iiyfd??oV=_@>tZ0zs z8tSCAq3iQgHdf8^Yg1)y$4>stictbjpDnax`7`r^pN#9 zXW#vD*d<-W;x|=NRik`~+a@;a8{%2|EwI4ikHXWJLgwK&Zz5wQL>gDSHZ;b~w~vW! zJ>!`>+m5=NW>dk+3i}**fq6z2_-5JQ!9OxGtmgb=$Fe%`>czebfHz9=!>%-XT>t2m z)PuIi?4Wf!3?LU^#FkkhQx>!pW^7sXla(|hY}l;^ufftlB#s;*B_WOq`_uJ zBn2ZN&Xu> zm)uOVxkp)S#;-6IP^~@)7)k!P#@!>WAqRZZSl&9;K~yAlxDJn?tOoF+x^-19mXn)< z%l8iC8VWjhqjo^YEe6u~G$Lrkmf});{|8D13*PV+1|1;WxurmxbkD*}9J+ag?tvg7 z)$g<6Ikjs0i3IRwHmfG@XR&~}rqq2JiP@*&ViqE77AIJ4lpPN%IY$_(Q~yvSc!wmp zqv@n;qXo(8d^vuu7;T80LP;sQcV3O(a+6C~=s;11|z^ zgjvCJqmmrp_ZLV;MM}4f!N;TRx~t4(2MIb9+k5*Azz?ohI8X(QXqBtM?gWqH#v=)R zaLUQmDc@V>g1%e0eLu*Tg-}!=XB^m00L=68TOLk8ujYY%rJ%KW8tFR@r}Sp4gfwv} zAnPVFi`)7H!aSB=h8$G7GE59nS&K6-rqvr9e~WSy%vrPm;xJkx$DJL%)+J#&+Fgs| zJALe)Jjs)PVqTm})`>4ywt-QS4Ve+`}S{|L1ARc93`A)@5c4=<_t%Cd3rnqzs* z%*MJvAlpja;vDM}^7pKhvgl_nd!eb1n{>D!dwzin^uOO96DD5}EAC{J!7AG`n#@5* zjgB~1=A33|B3A8xHl^dn#hpFDq%-DpjYqB3W87FQ@q!#lLyj{e*QV8qgc&L?hb@L) zHHST`XYIum29G`aJs%JT4WS0{e4)oOzGO(UQ61MF zUROg+ic!30%j=~l_)YJK zCs3}dz`2UJp_Q2r4YAb|B_1TXrb}Me6vbWD+)LvnCA}ft2V&z#oT0^+OYING0AAV* z+Q3CeOZLbI*kC5|suOiJ1d&r0Ml5*2284aTrL3fgQn$TS{DLV;hWWS)0z*vzKv9nOmlTo`9GKr6y)dOykK8$016%?5cwyO#s zNCChu1hkdZ)aLA{h8Q~o7olLG;fAN!x13YP@Q<;z<@H&i@BzIS!^h!^9RRxtBQiHs z>5y5ll4E~tyT`y;=`}LD4INZ%)3%@Au1s!NiYtoBM_p!q!VC!A7mv+Me%gI?E7biN~+dHVhagP`N&k zww~AHx3=d4p3SZ-M%gV*i-*QF95U3X?+Ogup@H2&ih)IUo@uShtk{6kDNC-#iH%Bg z!4eH7y>jIc=->*^WKh9x6EL3%b)ChV=*0#7QIgT{66!n@is1ujtaV{|LU~bn>&ulR_3j84-9U%H zY$c)}ZoZ36hsj!1`v7$;!&|{__WIHZ28*`&7-qF{*qv))v5AW{3iM`G=H+~dJWri(8cpK#tR;`(>|fMq?KUA38@+Tvh3%%T%<8=#`I&~*L;CH+FUMs zSmB5xDrbB5Zs8GYEsb@m+uGy{7lkF#)GEB7Ehz1EWQf|e+oJd1C`gG9|FV%OVQa=U zQ_djd?@zLaJH5Y@;LqG<*I6{-nRVxrSoO0Q0}Z+y=Qg=Wm(SV?w?21%6zk1Wn=;ox zs$JuKaYKz(;5qGg#?;Jm*s|>9!2FRZC)HuI@_prj%gdzP@qU%dljzmO#ShA4|4M(h zw+}n_9_H?_B5M369<+I@8e-9!zf8e`TY&di;y>|5a6`vk2dwvuPaJ(MNH_38^@DVL z(7aC(@YqCsxYf8yXz;@=;BxpR59)G#T}96GoHSr~-%h+rt;zF|x)d@P>rS@-Z6{2( zOBN4Db(+MA5BvdD*w&g(0b-0B(I~vqeSAyIaCDQ8kNy;b=kCrF|Dnxohp6p;o zz^n^yf1mh3IZtM{cF)LT<^=E^NHGA+8~!g@rzru>rcYf=M+2)4FyVxqZ!@Y3@swsI z+Ov{IVM7(l&ZTRk#u5ouMvIv(Sy)_1){QS9*&F{f6xni?kn z?p)w}BdGNMONlg}Ji^mZtyX2~CKS9GCI8`Kaaig%!*2ZZ0G;D#*COP>D0!@wPSk#j z=V`%a8W8Lmhv8B?=I@!8%B;rMLhro5t>-3G3;E4bvPh2Zsh+6;jdoy(N$Hc95btz+F{qU-T1Q#&oxVY2$tWgHl|6bms27r8P2fJt7oZCup z&-K}%FV^1$xBAil_M_#D_z2KQO5Q4c-Z=;T;au#hKx6Qec-zyU zLEx_=KnWDxU9`2`~uIngRZW35)0Kh^pkKjF|Z;P1$Ey z>@hx}z$ob!sPd5o6HII`!r)N$7RuN1X4Dj$`q`uo&wT3O#Tn@k#3&u=t~z z4s=-lOO5>OT&V=uXrzs5Sd5p_R|CSn%feZ`__fe1#3OyNlNgdIW|O64mBadpgQ}=% z8qIT~mg8zxksm~ma<$Mz)MdXly*B(CiOxu=iYHz5E>XdGrZ?iSmm|AsGm{jA)h>?i zlfYM@kcXK0ACh6Xksa^3XyT*3f|!(~mT@B%H}f_nE6-pfuz9Xyl(F%g6yEPE%wf3U zk*GIR-pAfGTt?0ZMpz6`2E?FMf%yy zsc61(gs4eTnyX^GH&fy^t-RGIcjks)giuciCs&u7v*`fwtbEWXd0nVtHIQO{R@Upt z=xRbiYtX7UjZEr}_cwp-Arc4_jYrr288HCNkI^($GM(~-j@)~l-l&bGAYs{Wl!i+833NKBAL2I?Fe@}9hAn={r=lO%}f$QJWXj@l~WK62-mn!tp={>#oyzE_% zq&50f)_`E^6xKl&!%Cnni!BD`!g!43>CÛ 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�GSe(Gi^V)8qSzK+goY?bJ( z@iJ0ANsvv|A~LeVT)}|jFZU6uqUoi{|BY{&8@o$?9~p9U4t_4c1PRlJX95z>;xHC{ z%izZwLHc~JPk?pv&XoJ#iIKUdD>pVB?M||O%1aC+4_1^Ig_#Um0dqkZU7V-vbwY1&u9LYIuv1hkhOTvy}v@!nlj{~Fq| zkX_4xj!VlicxNlMCgPnL& z)1cJ%-LnPt=4T#UpSdO(#n-`OsHd2n(+Q)2f_iExf`JL_$EcB=0Vj4Bf|~9}{pF%hN2A0uM#o6xyj}pI18NfJ8i@1a#&=K1PV9O-)Jp6UEb`F!FZ-Z%UHC8GJk8t{TL9=LfFp*AwPS zvMPofUMghP!B^`fEeghYu1r_{7IbiM@KesWTePhlDOZTcDHn8)GqLGt)Eqj|B$4;ZI57)23 zTp+l!gT&GGs=Ek?>MR^SmLcP_|2Nyl3e7)a@}en6KYc5P(PE}LB$Z%~QBuQ7>}s|A zrf5XE+ZxGs#+;RN`7=841`!2$^^1*RrmtgmujIDP@48J9Rs1}KZI+m9(n%mJPoV_) U=}P3;zed)&rF*mRy2Y#i2g?a7YXATM literal 0 HcmV?d00001 diff --git a/generated-icons/ios/icon-120.png b/generated-icons/ios/icon-120.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9a25b59445322da27ee09fb7cc1e019cad528e GIT binary patch literal 8250 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 literal 0 HcmV?d00001 diff --git a/generated-icons/ios/icon-152.png b/generated-icons/ios/icon-152.png new file mode 100644 index 0000000000000000000000000000000000000000..245a12ffaf0ecce359b40996cce798d3c912422e GIT binary patch literal 11178 zcmV;bD^=8qP)1^@s67{VYS001j-NklOR(F3id23xyE(^xqF6!njf;E=}V;7kaYeI-Y zpE|f#Ve}*##Dwt6@X~<{0@fvhx;Q)+enlLfi#9zvcXfnmRT)INu&&@5a637laK^U< zXSWN+Zx%)Ia?RK`N5kO@Z2jVQ`0MzND^ir~8e{wk*Kdmux{b2~tKnSVcJU+e^LzE) z;|&+TUr(F&)}ndS%JOo-8E17CrKP~-fDkPM zfFJ;cKwv zrqgK+kW>(*%+vNv_QDK8B*dLX0L~m}0I&$_wCnT`ZtlC=!7cYd_<9@g;BR@LBf+1~B#Ez!ow zG<+dIiZ&$KNc9u9wPH*Vf`JB?&ln0Yq9=0+R{*722-L40jp9gATsn6y<`v9h+t z886tRo_v)t{;A=wKlC5C&-)0$DrvcXT~xB7Teohl?%djYs;Sw(4+ewBx3)H$ot^Ev zC={+rE{!d98Ak~&Q(q>ykVK8@*fk`;6+7lc0lDCk^(dBvAaBnlzbI{C)g|~-W%z%p zESp|JikGtM_I7vb-L1`LP!1kvP4n-Q@4ot!c=6V%>~nGbdc_VCVAro-uO54hPd9$B z@muTS-195TqmPXb4*1sAmIRhWR(YtPV%?yUVs3Uik1v7Eh0>DjMKVQvgvY&PJ;4?yMh;^o!#we!Hc%S!vs~gaUXYGm@pzxbl5W1Hulf`ytw0On~BFpmSh@{SAIlQ z26iGPt$qn0(2l!YOT=_}d9|vU*ssO(kC)#1hG)c8QL-y}c3F`Fu4~t>X>i@x`rPXB z%9q9m2czBHoq9fSi5`En0E*A3D_%xCo>pltZ;T23wA>po#F;A}T!4*iJN4*Rb-8^I z68F=nRmGToxVi3jfL878?MgNq4VPBFH2L<;&nXwiwQEI7JV{9=fIg9XH@3dGzP|R+ zJGbxF&VbJa)?(N#J`$v=%3wXnNI@FRMj$~j3dHGA=LUGFe)mevOs<2|m61gL_|GKP$ntF=3n! z(oa1UakSpvK6Um1S7y1S-6g^zuq}ok91Jd724K0QFD@5lqJ2zkVO$?_<;Kv(aCxM; zObAx)-@P*#UAXws{g-Yq{Z)(WSa$P>R80E20ufk1_?kjdKP$>{ur z!S2S!7gpc-@F#@yEIh`i*?lgZYtrWW>HppR!}Dj)J#+8g#*{JXaO+ZwDVroe&s@nx z7q}v-1a(>zU@<2tr3M)Q8=(eC)tCSmtv189*!JN0H+K^}3o!~bI1n%((_zxrS&!Dw zR{NVR)-&`JzzPP=;RcmiaVaXOxzSJyr}-Z;KkB(I&G(V`hhEqKLRjCs>riTW^W(mBB7aE5Qi!7 zVcRMJE_I6`h#;nr56osX>K5ejM$RtGoEiWsx59MFO3s!BT>QPn%#{{H@q5$K-0kme z{MChXXaDT>?K{(=kgkY4aK)UC3zOa$A?6Mv>Aa34)LqJBAF9U~6Npe1QtHpB&L9qe zE3or&>@n9#SoNtePc_EloGRr^h-SKc;el%J-o{_AyyM}IVO41vOghhG#KtQ-?^+rR z|K#q*J?WhfC@&9OvoMzE`4`WtJ=4URuSE(4((F7UBN3q*iIJ@jF5}YztVO6tQ;r8* zSGIre%-WepH@7xxH?O!TaM55`)Zc6}j|z|H<-OAjmj-nt9B>5X zl4{c!`>0H7tF;s6hRwXT0Y=2XC><0zQ7-2$S60tFy8oS5eosw|w=0_sg9+(O+dr6m zQi!I*|KAr}A+U~z(^5U_7+%TD7SG2=ZgKEBajrz9t14Aw)fy zep1DLrlmJyl->FM=KDrV!++b|mCI>)(T}~-Y5P9Wv}DXobvL;^M*-NjA=ZOM4SXVf zXq0#Px`^~Z&5!oKUwyX`tnfRz&iOM-kEGo+dmoc^%=(6w(){`uvN1BYZvp#f(AcFIz91Iz+#Yat; za%XJb6b}f&KD4vFEgO`uz2>3(MS^4A!NFll%2Li1zPO}@rDiOs*WD7Lob2w%-w%oZ zy7_>_mSV7`-u}SZ^)u@exrW{@bIz%X4hxwp_ob|%wFJvwuCeLQ6!0Hes}LsF9$84JjA2LS0XM*6JW zbk3{{Mdi&Xha8VDLimD$#f^K>Grpg=ff}EG6p6z8VLH8ZI=#RU zx(4(=u1rCUB*Qgc@#!GnL5lrU(P<(W9*6_7HzkCY=y)SWJhD_j1Y?@~W!Y52;=S_y zq{}PID}`X9^G)6^w?wz~(WTPTb`tdc9G)D7r;aD8!)!*_TU5ulKhExQwwF#1|9~Fx zz~!pObCIFJ6~C^Av3hy5T-0N6xnM%PLxs=PZ=&-&RGs1&7~~P@us>iTk<&m<+!3C* zLsVP@2o_|N-DNCV@B*O+ERY`pm!9Z$m3q1;Xsb!4(!E34U~iFoM9kZFsm%RHlwD~f z;VGkThFW?RLK$eB2&Wh;APLZkfh`uQGLzofT|U?_dqGxn42a70E(J2Ae@U(#J_JCxK*Zolub$XP2byby4ni6S*x57LlCaYj4p^W%U-^xkwG<)tFL~ zA^I>YdGUs+;KROF5g6qmSNC5}glD za6co!(pC6CqQc+4&_axl=*s+gVf^%wqUBtzM*rol10Ez+`YJgj@}Z zl`u1zI>4W?yRcfdNsSE`UJD5>7lO`p{0t0+9{UC74S`1iAqSj&()eximJ4=UbroD$gC8a#{uo)TQn`H+=++f2}Ip zBwh>C0@R)tlmeEE_rXQIq^t8T%m$;MTwP(i)4|*f*ll~wqjp0!Bbf{`PJ*pydyJEI zl{$m=p0&vU#kKkM|8Tz4*<>mRgj08;+ojT`!%hW{wfsDuiw7Mq2w)gA;dux$+uTTb zz68?~U7pO|ECx<22nZt`Gnr%-kHuUu!m2R`ObrqfBr!vO%O<6HG*vLf}!f(MFL zkrrf-q0ErqaFQ*`v<-pHRuj7?p~@p4^Axa5yS#fsr>%0qu^3j1KUckh<*~{k2(rC= z7Y4dEMLYmxAVOHS^0WC&)^>`qw@AZYy|e?=E&weo)6_HTHIuek9=O7^qz%@oLn2{> z?Gef6Zeu8Tm>ZYwyGRWPoZ(IsM?cE8WxicytXhFU2ARu&OlwJK(3?2$mXY0*8yfaL z&}^|5Y}C{fv8D}*XB-AO{kC}d&?Drr9Bv1n%?3*|T_149&FQqH(}@niwqv!t5>eC5 z2+tbOI38RHbU!cvGIeI{GgGb(@WBhzGXS1zn-4bsIw;V^B?&5A7g2s{Kv2yGo^*rL zOoq$O5!#RsKqbQ(p(b40DGR!KU(3ax{I1;({oxyk7s&2&_8P$h z(bYjMpuzzQDlh?BR~KW`?S0nBt&plx-BbM^R-53Cnn`6_1`W9067kOC@fSgH1 z`harEg6a@pAom6ueS_#6;E9sps;5kDT6O*n7g@D*Or}~pq!~}y=KuVd)dy4gS(mn> zbZ>d1Q`>|7eYLh)id8+KmSLyUblF<29Dkru^5WiV*T+PD<5eX1o8wTV5YjOnxj#e%_E5T2;aks^v1Q zo`YAjoJXcVW~eb~D!OI0$P~o`@db9y!*+ z+coocQ0f3>2TFLP8k8|p#X;Vb|0@M7^Hcf0ul^u&mBhH}Xl$^BRB=CrgO_A&7+$Ub z?y}d{B+c}w+Z~FzjelYk8PZ>D;FMr-l$w-)&UPQexdP zxme46k?K$fqMWhm!GvwxxWme_(rvOX3jN^nP9QD7Hb`D%Y?W*rSVj_~NB}a?4Z^rF zzkt>Ep=&umX+-3Up+ElqtC}F>j-eeT^4v;Vzm%Ux(8acMj+58ov|K z2u+PZdnX<`69#9pnZMz#Re3GI3SO|mV8E(Dspccq)Rr9)qrxc{K~g)WM){FcU2Y0= z7z36$si1>2-uEd3AC^igym&(s(wFi3rk8*kjz+91Dps{~fPD(j0vbbR1STC-rLI{5 zO{>Rt^)GBrVDwI0a?HO`V?+_vaQ)xXm%7y9T8B$=K=Y_Tv4J2 z&{E?=oHi!;aK!e_>>@jq7lmREwOqziHxaujCU8AhG>-`4cq#z&6t;HB@k!ihic2JE zw0BeXSfJUJilj2+bhk?vc~DcW3dedmx*rIx&X`Jb4W`;lGEZxGFB59fzNCS!jl%{b z^nd|*_}EZjb7`pI)vT*@I-NBMmxawARguXO!zV6YbHA<)p7#tBFZcfCwuA&^#3c0q zpnf7#Qti@dcPhfh;~r6;Fm`byN5}LqsB2zN1yyYK?!1E7m-Q9?NGm@6GNYRkndo4|OAEVl*UL6S1HGnawQ=^;ZwB zV=15^8w?F1QG;ymz)Tb{9*!PSB*APpSy&L6Z)Qo}x{d_|iP2=IkuT87N~h@x z3Z?u$VoqDkzgl7h2pNs6kkAA?g%5uONnJ$x;hobECEAj84-=q*;ux$)seWg;Er@d4 z6CI6pTwK5v=;6=*idmapF!hp!5Dn9xD5vY8!?9A+V?=L*5{6h2=-AM$m0-5y=@F>O z+)-pJkin}IIJ((wK9>X@fGX((gz)mRysn=r{uD71m<7lg%@apDQWUJg}5ll>{%`pJ%33KU%egqbbLYEWpm#o}cKHwVGkM%v{%VXi)| zi@x2G$9X6xW+!Mw+j8RH3kk~PoGmR6*&<~y8nALuvfDQ{+DUZo?XnbUi9IqqM!SHn z?mnVXWX}&)K=BzYVHY417i8+$!i;;}1whyj<_S8CXSufs#+Fuxtf&fB*NytM%-9Ju z+7|O1M#!CZ*y6JjXjHCPKUg329-UmBt&6?pLeK<}bRSu;wT{;N3N$-V5lT%qOvnY$ zzN$>E$Ve4x4|BZvc@lNir31SKsLXc3Vcek4@35SgEy7_zGEaPYQ~uj%x@R zElf4bwNq*i7YacX%V+iD{eJ3!N_2J?2a)k?P@*0=%3$pX%90uFubk?X4Hk8RMz4X- z=9#5pDEAJPI|FQXMaBIR&e;CW0h=DwY-ywxst~HATLq*6mk2#%^6qtOTi>E*gJDp` z3N)|%&El2}3v;QAnsh2s#2L$^g~qKp26l7Q?JF1?A53O#H7WbMV|M4(J^hAuA$?%R z35!a`OwuHL%WpqC@4mbgIfD{SvKw4bDv$`J+fIe2x+2Ovx7~@TNq>&PC>qCS&TfuX_iA z1ng8q#+w7kSbjSBH4O8zMcTNAQmMTlO0_Sl0R!B=k^~ubtlpR9td!pSQofxXn~Us{ zd0iOyZ`2r*z)gWyDBwb4Hjb}q({}``*Qnh@U5o#fmB0_y#|D$CtSkrW&2F=ecoJl4 zQap6d>vVcrNWE)mw_*~w>CwxgB|@1&iS%jTY7zl6ORFM%FI@mSDlmCHsDbFtrpBP5 z`g2T-I|zRuBi$yVmAqsHmv48sHWPIPL?08MMQp>t2O8S@$|G01m@Z5>5RZviH$@Jp z#9YRHDxpzy;RsP@K@G%dW}PNdY@~f>h1?m{1Cxk?37Z~x)N2`#8IZa1ymajJOW>w} zz{;`97U6!PSPMq@D@%Rm!jr*$RHTRi1NyPl2dF4QHtLc93w_RmT3-twvo=@?SQMyY z(x-#5>ces9(>ww8biB5GG|+o?7y8;>2r|<^_l_dA$-KPTS1qU_PNh^i!1BXe-%ieF zeUcKVNousnB4EQR(yR&V(+rgtyr_*qX1}`8zL)5YfMdl1h6~gBY<8MK)}bPKdJTM6 z=`qu(rLHJ*fs9ZM*?3Py`t9ojE_)3C>oJh&1np;#k-CnmN|6E?yd$xX$)8FU={nwa z76Mkz2$K@3JX&1>qaS7>jCFC)i*aB z=fqSe1*mzA{AfWQpulJs^sG_Usjv1$s{@P|1r!2}GuR!-sJ!_sMz8NbG1B2s23rSo z04u>_IDVEkQrH3?hV|pXN7S7a6RPBlnnU&h_80_6$XIb>y^F;}XFOQTZyIqNWQN&^ z&QW9xSWu-x>31vI9b!?YV8n>OpB|`4@Uq7YAd^!}o{^715LKV;eyS*47mM|lROUs4 zZ?1;MJoyM%G}!zPFp(@4i-=06x$Ff4`zHTx+i$>jvOnZsNZ>m3uZ%XNHU885a@m{-W*cfO2Sc^i?E{L?CiNEH6JNE7%tbrd#uL5gE3B}gW?5&{VK-$wfdFcxs=O% zPLotVxWH%<`^Of{VmY>#M_FDze>V}RI&%RoLG|Wey2y@$Ovg35{liVQMvYgF$xZ`{ z6#=+J$WoBWU`N8V=7kIQ;stjGH2AaO-k)D)P+v66lm*iC=n%VM-TfS-3C5~n$<`lS zQJ=SgALu#_Qkfl*u>nsR^zPb`YoiB)p|7lXl^frQI#VAjNohmXztKsL3R>F=+8l)ItI%E zm#@b9*kVv(_bz68L7+MGQE7n6Ji(8n(U^576L2B@Q*T>{ij21A`t1YmeEvs zdX1I^HDJb5MgS*XO`=g`9=KvJioh>iP(rgD083{>ax2&Kj`dV-n8Nh; zB2SShPrd^X%~~PQXi(*JC^a~6gyX188TSBK-ZYI>i~d4DnL#=$Z~SnFt(~dZXq5P% z@1aXRP^ACs_K!BLNB=ab$!b<`LDC6~{T||t9yLZ>j0YYUj8&$aH00`C$MJZ~UV7;c zJ9l=#%F+N6Dp7hGlnRH6Ejd{B**E+CJL3bK!=)rd}P^ z#=~%QY(PfONn-H0@?c#!Oukhv-vl2n$(Mg;ePQ>+sK{<`1tZ5OgN077cI@D#q!=tT z>UA4Tr}^yV8~>`43eE;Y`H}U3b=O;;e_K8Tg?~(^F%2x>@@529%uZ5b^O)w>>|r7Q z;C2J2MJjW-6u6A~$sbMnD=$b9F`Yp(lu2ViCU<2v(~uZsu*`y$cF^E6k$Vk|W-A>} z0Y_KiCc;uIX3JtuVk~C2EjVT1a&C&4C%gq$8d&k=JPtD3I8m6&zv3|pzppFwR(xk8~Wz-^R^a~nX zHZRPN!|@*ktO0h!(d(_)@alBSq`fwJWq|bN0G^{f9^;~8KmCjl5bQN4!8Ycw<6<4_ zjzK<<9t!RZRXR4e0F9_HPJBZTPy@weHi=~wyuvicu9#i786_G*s=h5k5dg8m6v%SL%VYo{Vr4hWRJ!RYnJ9#z&Lx}9tZ+hf&G$_h;eV!oX=E% z)l9e917B6D#SDUfn>*jzpa*ReBE^ScfT-P80c*PQYgAy84JX zgffqtsEw7mxGiyZv)#ely>o#1o%r?Dm2d1*p8fXI*krr~5>i)v+Tz&E-$TPmIFP|< zx;j;yA^;q+h_b4rrnxEChrBEo<-vzMP-}+O2z6(sjUaGB_R=riLMDT0Txa|0GFdmW z$8tM7X2Vgo6={1V3XZl#u-BT1c6IK{(#U_&*gZOm7&1kFBkC(^%!4<-1N+SZwv)B2 zq=!<`>;ZH27?RCQW1PKa#5yV2i$z(o7xwoj4dX>gsi-7_kVZT)dc>;HOc=)o0UZqL z)Si@LcXKjPGyO5x3j zu~)!A?Xcb$`q;QPs?nxt_pHDSNU<2v9NZ+)`T88;P)?4W56t) zL5|{dgW`4&X#?WAh`7TR%KkfyvQMONRVD;> zXVzBv-v0h`7v8+`telSIoY#u2KJyu^vxdXsb9yh_eilV4 zHAjD)nU268`5JP09;tB7`Cu^kobxr=owRu^5%=!55B_;)XZ-cmRkfdEJ!`L7FoBm| z?YoHZ9YZ2-|EroK@0|7g45*I1*3Yb!TiZKdKl}R8KO6Rvan|0tc1_=}!4m)YWHN2^ zatkn`GznP-? z-3^?bY-yUNK7Vet98afzd+znC-;g6m{)A>P7Od_Sf?vDFieLC$wzU81^o2oH{o3yC zxMr-Kr-Z8@h*cDL@t_$+Lh)3mWijwNzg4G#6WbfU$|-t*z^RZWj(hWfMuCu zDgHV5iU{#Qd0L^%w&lk|HLk9#ln3L3@1A?Z%4K$q9k45`;b85$=aSG7*VyGte7A0z zKMRW78zIXhua)rvKuKA6s{m}DVpQF{@K*Q$-JosT^5@Yjr-o5nYt zz`%#c0o$d@i~zvyUPGuAN1> z6$LISQLp`DkMXGl*`vR`^ogyl>C+c33&FRzU&Q}A@h4h)}uLKcHWDRow9;vcCcLiZACsk&Q?Fi;e-JZ5eG93k=UVy}V zgOP=wTv2woVD-fdYlDrAy{FH=aqUTQRg@AVseH+8n5DrcglHtl-glWl`Rohz?K5Wv zpV;26S>4DlE%H*o2kYJhHHVy-5GOkV*f}2HfIWuu>)$pU+bw967*~r#-dMVhob8G+e=ij*U$*WhdmUzTSN#{P_2xOmp_8Zgx9S-^5F+LdW?oOth z^Gc&h)&r4c3Lwe^2PC)vXCy6f_e61{gX5wIBX=0Nzg$vS$`JD8QaDgh|$t%<{m6^-46&OVjb>5ecsA z*RLysJ_lSgU8)3`902^wi(++6G=JGN;!mqVS!{2QTSW6R=4ACMMV*D6l-{T@02A6r zHR4=_3`vbBVVqY7VH3vMNL`PYjyZKsu_?X>RCsXz_@M)fudlBc_fxxXg#0Bg()O;uIp+Uhcsivp>I&Ui_vgMCy!1FJO_cxQ~kEV_I;L1vQUS7h%0b`S~{9JI;@b(*4+O$*uwD&v? zt}doAaV(SBDDXk>Kv2X0XxdBz>i7UmC>yyw)PM4p7fZt>Hd-F2dUkgA@0LaRk7YIZ z((;4+YbvqvM9sijx!2K4b^ZV0Ayq5{_rqQU9Z<4dRVXUd; zU!y)FY!KInElBVM(?6lx!lk1}uD$Iq0#H71L=ZoZ{e{z})9J5=$jxo!UJ>$jetEC= z_I8_s7vC?+;(1>1e_CV1ukwfZtCVJ%R~D?zHPGa*9Cezo7jKEx-P`P~)A8hTAXjAjKpUxPo}X_Ud9U63me_roB=1$Xpxj4X4-(1uTla^xkj9 zHW%VHFZj)(EMMkKe6t*uFRWY~e1~(sW74*&P)!r&*U&Bh4^Ax^9*6v5YybcN07*qo IM6N<$f)96swg3PC literal 0 HcmV?d00001 diff --git a/generated-icons/ios/icon-167.png b/generated-icons/ios/icon-167.png new file mode 100644 index 0000000000000000000000000000000000000000..f933cd550ab42fd3a72088e25d79d9f45d5fb110 GIT binary patch literal 12547 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 qRp70KvpxF literal 0 HcmV?d00001 diff --git a/generated-icons/ios/icon-60.png b/generated-icons/ios/icon-60.png new file mode 100644 index 0000000000000000000000000000000000000000..c7c3af99f95194349af613cfd5ec55df7c7bf6f9 GIT binary patch literal 3271 zcmV;&3^?$7 z`>{bRq>^6?DQ|$1pZ1;jc>2R9Kg%)jmdI=GgF*+{-+g#nRx)feL0%+0DO`4 zsI(xNk(9OqGdVl-gN2@P>_jQj^1hAiN|K_ptTUWNX@3#V==14-YI23TY zAK7FeGp192MG(3SbO4+y*LoY-tpQA1q7sE@SI(WU_qMm+T>IID-x{oB&JVttUr>F2 z6HwP}+mdN}eT@Lkjz#ZaewKAD0xp|%y07hTJpvj^U_H}-s%bPz)#_SYT>0Kpw@nn& z2M6!2pIKXJ+ZM;cI-hdqhJW!~NkKfN7?Uh0kY8*2M0betFhywhXOu$l+<8Bq5Tff^ zY}VFRaK3og`0$sv?q8UI_=l2Wwd*@lj5m->*fF+qhKJ0Wv)*SDzs}jDW0ujJ)s$)1 zM{9cvDH*d8PvcM*)*CfaHC`&$!d~y&MieMJIL}AJ{xt z$bJi`#vLyAwb(C$jnT3&AIXNbtdKF;aD>*}49(t*y&_U+qEzfiQr)mz;)5@X;K=-X zz_2%^JkoGRc;6zMson0*OF#`Xhf@T^-OH-Pgr;WJ|J zF~)n8ypX7bfUfJ{%u^fCtTu({d-UMg?Ji*NKljW8ztr++c`gSB3_eX+z?{7#>#<|e z6r=Eb%WsU(tWH2G3E!qw>l4t@sg_DapT^weWOPas6p;ok;0!a>WT2wwmO>c6&NnjV z(@1kMyTvJq7|2+VA;iyEMPb+%K!wM!9P<8j6tGD}%IaW$NFyxf2TPRY$7j0&pxr$H zvk*fQ1I*Gp9zm9@uErEFA0)4D2B^~Xzyndi)D{N|`0`(0K~*th;xndjn{>TO2?di$ z6H^Nxok#Cc9Wavy#<1Jc#k7);x-{5lq@j6&fyQ zx5-O@+@yiA9ZWiyE-MS8Ij1pt&{*9&OkI8|A89dTg)-U)qZn_JG|P{3V8hCC@B_nc z^lu9|D14mJqNek%f#v*kSc7nZmYHVImSfXY8{Xu(Su$5+N*IX2SPhM>;ZzhDqNOc^ zm6n`IFCGnx0mVjI9(a6ZXwwcG*qz{(l8iVE9r>-;Jz3*HE8@&dd3Z$v(D&sN7rW?^ zp6|2^`Mlp%U)S@%9VVahS|kc43N=3Qs8Cd88gpei7TLPmY3}HY6bJ1 z7F0-Ge8w>5Ov?gGK|7*g(uJ&1yf^`yE!iA@6(lTXvVdoUTY@Z&NTaxNfWCP=!K4cl zngLr$`p=WUJ3$W)k}D9QUC<+{Ve>Pf{f7$>QYCpQcdwC0c%!Xomx35hpN6EJ2j3(X`Mi^kfb^=EIl* z<)!A>eNt1lovh3vSV{y7&~+MG)d!299y0haf_e3X`?MQfDO%|mF6VrkR@oH_5;Ozu zr7h)mZ^Dda_Z)AKmL`)5F1)Z|gdVF6N^shF2M_NZnCj~^ZM!0{?x8yaY!#4$GVFgBh2?7arb!edIMW9s$tx0%o%XY^>Mm)bEg%&k*PPEj=3t zam0r~M+U4JSj)C$+4y83#dzVk|MDV4stP{;d>^FT1qs>!HYQJ7rvU2$E`agi#ex7s=Yv@BQxUo!6;EFF|yKzG!w9D@`Jn#nYnIQ5eOwA**b{euO6E&-gm#c7#! zRRXIExj6t1%TWZ9uPeknD~WjmlQ0;Ww(H{L|Gsv1@qneRN7X9pvng1+O`~-HY&6YG z*i}VFF|H;~)0=&~N&A{vLMXIYc^PJ4J`g?v9!$-t{%t1vc~Y z^YogaM>eQi^0Ye?S+$#T`a;A5@9Aw#`qSoX+}&MZdtLtGr_ z%o?HJ+?-Oly!P~STepc-QPuVPd%NAdt|XauvRLISA^Tv!7Y4k_ihW1hG3)8mYKVw+ zU5TBC`*S&|-Y2Z`_W<&xpNL!SqI+v&T`77^!vmxALtu&fN+5klkyKB7(jJ87Gb*+d z>+dBX4?%qFLPS8fxiMAq`Qojur>D1kA`PDa{Nb;Qt7p&Euid@dw!Ib=y>Uuu$fuRG zHbzj6c~Xc`jEoAhR^j@Hw5B$7wBPs24T_BAWv;xV0(Mr~oj<=`-@WtAM~^?Z89o8< zZ=UFD*HHcHrRMe7gYN3qR$W*0#RFhRMjjYuVB-wP&w7}db1N8%lYlss)uUkyKoPN1 zN{RF5*6aJ*yH`C}zbIp7oAjnQBI?VRQU3Dh;*CH3t-CR);e++{YHeo+=~blg8ILsY zYNkvDb7jE2uwmX@FzNWzI90}RTKJ3y?Polj8`ElUZ?QYunZI-H*)vz@O!56AVzuhBVsvEUQPdmE~K=*zzE;Z4?zB6Z10$B!ElARvL>EhU&cOl#pYwm<(JpBJ)!Rik(rHJVTHgn%YQ?;Ok# z_W2?J2h1(T16&Rm7E-CNm6SIG$WMFKe|+}I>C#W}{tvTY;@7D(E2jVe002ovPDHLk FV1i@MO-=v+ literal 0 HcmV?d00001 diff --git a/generated-icons/ios/icon-76.png b/generated-icons/ios/icon-76.png new file mode 100644 index 0000000000000000000000000000000000000000..8741f9a26e38a47d74a7cd1da0bbebb6589b45ec GIT binary patch literal 4521 zcmV;a5mxSrP)e1(%d!2i+xP4D`cXd5}^}VaQ z8O%sZDGt{F*vm}n^R3Uf9;(FfQroppD5)+gCC|xDt^xx_pNmeE|3}VkhN+~Ca?dJz zgSlUQ?ys9NYIYfVPOd3XiZi~;xwy?Z`-BVjQ48G%OAnv_ySe|;OB}9U1IFxlONq?@ zOEAVdo!|Y>!6jhuyzJVihQp;*-GQcQpzT_aGOb(npE_Xdz@o;I!S?~-K&Iuf&OYGi z!~g*23@T9puWA6sU^<=bib~v2jK5hw{J;n4*cSo}AGq_6JC6)Tqt}Lm!PA^E*xlQK zw(F#Oi0RY^c#a*RAOaqip{543E+Sn%$@x1SwU^{Sv2`hBdaA~xgX17FoXT#>k&0Y z)K;KIS~pU+Z(Y}J3QOJ8Xnm7+ZL`-k(_aoByYQBJM+tU0291@$+InSUZT;%@_BOO_ z({av40S%aya<(HG0-ON^?RWTtr6u-FjDb^nocK1 z{m~KGlboeuR-1j;?v%jJ1zjkYuxZwA+Ks@PF<9hR64vzWO693NG{tDsX)qiviFW#i z#>iuF_w${nR)(YB?CflHj4@FLD;?B~Gz*ua{S})TCC!+#W=v5TFr~bE&NgZ($+IEJ zBemJ<(2dqML_6L8d42Kx4MSn7X@A8zwN{`PES9ln-!TWkg-}lWa58Ip$03N9vEthN^YRR(9Re^72T^=`)p5>PhV=%=2>MZmZs|BGCOMj+9>F?2j6U1mQ%|PwM#g7p%9ALl6U zbA%yc!r8Q)@_J*TWCp5cQ<~KS+Vw}}U@ua3vmgby^J)rei=|e7kZlw^sf?SxOx&N- zN{nP_B*DoQ$Ae0_Ve0`H?PgK6w*!?07E>8$cF>{KqIpW6SAQO0iQ7$4Wu!VJWx(HXT*umXPrXoB6$xI`9&8>ry^js z(I7vl=v0Y(iDef7D`Lyp>K}R10zlXe+e#@(Q(pucIto_X8(thI@I#&QwbKU+!)McvI$$# zaV^M_nSzG6GX-$4VN%2x-jZG*Pas)nkleYK?<9gj?_r-%3`Zbhoes#n(gCzh}y zy@wPQvtVd2oimky*3W9GHew*V4nQzqwTUMAo552eGSH5ej+|IGZo(WZ%2onvIuJU} z>2wCNW88*7p(MQyP%#W@NqffN`L=S*cSrHVY79*u8_lf_`Bo6EO@0$!3tAhzyH*ncG8g4Oud<_uSSs%w#m@ zY-2q(L&7<3PVFQ+1e}v?8*o!#{yFL@vfcvHjk04jCSvL5#^WrIRB_CX6AF#fNM_zU z3~nPpR6hp^;UXBMk6uQRA8&4FRCPo{I{}G~MF$WIFchqpUH7T;|-VQ?rv zQMDGdU8Dk^O8A)aXhm_4W2)Hy>-0CT9DXc(U=>RE#{f)HBtV$K_4jSD~6QQ=0N-u(@+SYbpe`gBg|4v|O zH1NXAn-(N5IETStXo7{h7b@kf3Ghh?pn#;nZSH~1x^HQL&0%-1XbfW08KJm{jX2c2hENSCm zrKCa9(bBM0A=!NrNEYv+x3~akCk$8&8k?y(70ILSLl(p_29`v#A|D!3o^LDXZ30rr z1ke`IvG+DPp-kHnj zpfgPe*xxSe0V_5s(9$o_MkggqCM|HGeM#vxBMN+^)Sv(Y*M@T1F&Ft*PiRw;m0?cC zq$b9E(7=P`#u4qhK&_dwB#veC>JOS?3b545mmH~{K z7BOqir!?k&gVqX~(y|)`pQh1rZH}*+rjr(?yDiiMUDpZ9GPJ`eNS@mC-36$O`Cmp@ zlX#EXaGl}MFCuDsiH4|8ZJuC#CrM?5C5g67s15rDkt|XLb_@=8*hz?kD39VR56%Q^ z+3DM1#(l}yGS-eNqK2@nhGrxM%}|_snlTQ0`weuR1aU7nqi8!%DwrHLZZY^|+0kps z6fAFQ%n|;K{CfDpwq~m#^=rIBz`!tioz_c+c2K1{<2$BUacnkJA zamH~j3MPiAxQTW|TTo6=j#VYF+*@&TZ8GLGAp(T6Z-FxJ9D2|+TUNDxY21}(_Fe$Z zof7~t??H95MD4WGV8a+NuN@Px%z$&FmL^;Zy%7xI*{@I9fJj>8K%89Jv$QnX3^2Afi_uV4M9R7Szl z)MfKJ)C|zM!^XL6_!%kCp2zigUowHhyI%$4I3?!joW}2p1x_YuJC8B8um9f`LfbHQ zOB>aU2v!7?v_}S<2Zh*l1rhC6kT9arLngtq2k8O`^}Djme{ke+}9zYFRU2cPRW3KHU7)=$0|6YsO!<+q2?GLWvsGjAj4h_X@t&Nh-yJs9x83u1$*{ z{h+?Fvpc=9wpI&J(tY|V4;gc3m>Rby2_5@T4vn}jnoilCS~N*1yN&fVv9+~xsmb1=3UmG`Vn84B0&bOMgr4?Aew2?w##dzwzMu+xRiMtPT9|+He2Z zy)s(iSGRXMXj?7QxQK#@KvH_nGHh?}Uq5qUrT>$DwgJ|?^Ntcf{h@eqf4_TfShKzL^;$T)zT-?kZC7ZbvSDnegLW-X4yLpVHHzO#eIw_^x+*lWbLA|%VefV4ntlp+HXogMyFnOe^ zs@JNDKdnpd?z}Hs`0C2C;ht$TeSg|aU%2nW+Tx!j^`I#wF1rVQ{}**hD*3!@)l-9^Sk;xcEtpjx zv&5gU4GlLJ!Aq`ZVQT+usJ8@|r@8`QePHRRgLb5~Z>Zg!!^y#}5aI^o{LR$|>f?R* zmdpgdb`9WV_i5$d{q^u6)(tOps(XSdxG0r6r=(ofUss`v8JI7DN9A3qtS^VMDg!Ky z=`RkLCMhBKE@S*QXZq91;v?C~4>ry(xlb#*Pf5@CwDSJ}m;Ezc<$GYN00000NkvXX Hu0mjf#aN%y literal 0 HcmV?d00001 diff --git a/generated-icons/ios/icon-80.png b/generated-icons/ios/icon-80.png new file mode 100644 index 0000000000000000000000000000000000000000..2c3e6ac2f728418c1d65c30baf4c91e9d2c9eddb GIT binary patch literal 4750 zcmV;95^?Q`P)m9;5Z2*i~$oB6p_h8hCm*~R|gVC@Ik?U;G_621bq-PM3~^K z555?pA_)}z*bsQaqws@{FfnQa#Avv*hDs=oTxrB+qEdG-bL&mvaOHfv|Rm0b36}@vHRV&ypP!C z;-0*Ijl?}@sWmEfD9-{1ut?;cx?d45FyWV$U7Z?EQi^2 zQssI6SIo25zrFR%)iakKJm8#X;tGhWOIiUaP7=V0-?u+McqY%Ym)3@Z^Rw9;#^bTr zUx{mi0yx1{Q?IAD5tY)1)IHqKR>t2ukjo&`zq~$ArC;Tc0Sv=zbLxz ztqq2kN2A?xI-4>?EaKqtr5{Pvogc1s|Jdtv?%hAwTOeYm-teGNkdWG; z6eMzPRBfBtM{Oh4*hp300O#&6_eZOXxs1c4@30^!MvhiCEycTS;CWIY|AG`a*LS1>cbKiv(q#=z3)9kayqRc`8Tqroe z>8dPcTE$kPl^BA%;~iL{Att=9L9;|$^ZaHTGzc6PJi|JaW>RxReTy8622uJ^Pc`PzSLQf{or6GwXIbbDPyicNn_@~2 zqVir(*wsHuE9;u0~gtrrysIdV?*I(N=ziOf(B97Dd zmZzvP&x}wroVZnJ0%_-Zz|{cNYTllK#1QFkXYH_dG!4VUiY>i`#~eua^JFR z?i#}2L~R=Re-qu8v)wVwZjPWR3Tp$C>KmdPfL<#LZK4Lz0_b4zSxAFUYV(}>Vxm<6 zn4ALx27|3N$nv92?6sBtsdX6N8bK~tE5^Bz+9V_1P=#e#M2#&AEdzaZHTel>V4vB> zBQ*~}1nit~0B>IF_=H!Yi$q7E)Jo%Iq-UqKwT!HfM8Z0|_>cnXLYg9O^XlwVzx;mW zjm-(wIqq_fQ&2M7Bmyw4x)!`dq~Q5I>R`eV34j<2LlcPB3${|*gd>jpS}sir7;I~Q zG94WPuQ))F+hLrG7@EKf?&Iw;9+(_MMlFF8(SekAa>_~6qdk8YiJ! zSw?JQIDhM%98SFJn6Tu~>vV}1Cc)QI@ccH`K+|8L9V}E#)TX!nR=5FEJ25wo9nTp; zfgQ-R+|`9Bgx?jU+ys!?s{j0Mz(fy{#A*{y9zRHH@)-I4NBm9r3v;)xjYP}g7 ziFi$hM$z45daJ==>T(CA783)5 z#!XB1N3I{({MZa4-R;!EFwN%nNI<14?y%9iK^W(TgfZr{H9HYoNrGourjrTHVA1_G zgKi{nn5M1NZX~nS5}8cSl6hP;xR5yyW!H=s4plV-GpLv!;HAZzkqR^~P-CUS-Ws2< zkuD2DtK*GBFj0|I6cWf{Q9;HyFgeo|zpR?xK^f|xn1+;v#^#ZuJuoAci&T3lrlIXK z=SdqF026M+SrdfRyy>Rynkg0DI=wE}g=Uegh*!cnO!ww8SXMD(VDqf=iv`5>iZ%lm zX9e}ZE)J||Zs0;{cM234BY06yj?F5(d2$WfO3^D|^u<1OMabi+PB3mYrXa3=t)=F9 z!}`P-m|j~D?Kd7VyV%}|M9)u%P~VEHIWaXi5?myxfbU7$hW#ySVlvV^9+%cOQZ^Fl zp@A8q&ZLQfCeCcVk|EORH85)psn@-=L|AApt-}~s5VFKHE+w2^n$(*DC#Nw&g3$U= zm%Bt*rGfS)?!#+q7q`R)rlqN)eUS`|d0AT4rg7XHY2zibSllo$QUBd<_GPMH2FwwA zN^dg0J%ys^sE+Ya@_Q(Lt9Ciec3#=2u>W*H^a4rSrG>V`IS-=GmVxo=e|>Wgs)ZO6 z=k{7Ad!oiD>W@eibh{nMJ1U9f+szpAb=tS(AiZo8ffNH1YMi`TVDY&=iN;lSEF9^e zPi=GVo50~{WXfIQEl!=eTiCXaZyr5JYY3k6Mo zlT`=)LXOpovw>}#m0de zxEK?cfoR0A!~9l&={Y^Slq{Wstuc&(WS4OS=MY=zU+bv3@|Kc#C{;U=xC)2P#Z@w3 z0VkOcYX3+US$0&SO>%X%91Ck_>k3$M(8>o7(O;{5v zL;aU}ORG-0k+p|ywd5TDCYOAuiG43}2u2q!{?jD{Csv70a7#N@A%uZ?r4wOf9K_H9 z0r9qkjYN&jd_{_ZKR$o*SX6F$Fo)Z>#!z%RNfkeCQ8q9Djyjc?J#>OK@Qiz*F`RnX z;&_H~P10ZM!UzxL>Iac@Sg99w-x|0?0L+`52arL%cBkT8(H0u8-zgEmF`22+%X(M9 zLaV~kK5_X(VnUVuF#rV*15|mEFGB3f#be2X5T?Xzs z0j=9iGoh*wc6KIk`gC8N6@L*%RN6puaC-vcbr_i#60S&!7 zNji?wxZk)jgD-E6m95nAoe5XOngab;nk`qy`C*L>Z}iR8J>-lD^m{#6rFc$6vN5oM zv5^WDBttv3Fq}9J^Z0^kDCW@b4}gma5a1qQa)v`a$7ew|BbqpXIs&j|;vF#c5av$X z!6F8p`n_C@L+4Rrp3X~&>#67C)l)+miZv`6IKn~&n$JS7%nPo4_Ug;8TwEMj15Y!P zgf>wC(GEZyXu3cFrypGq2b*}4Y^%kPI!LxrvQEY}OykDK!>bn;#!&FIH}=&yJ`_W% zB5J2v8ziie52A#OWZ|t^yNJilsRX~{=%_P275xE_^qq(X%7CXlwC`YI%|H^e7<(oh zkpZzb&X((qNtVT!OmhrTOEcDyPbwud4(`~*uGKKmsk5_*l!;aaNtIFi3iYi(Gwd?! zZq&7%>Y7Hn6cu^SR|96}ROq<3A%Jg^JLyOSStjzyZDR+i4fFsN^RX(Xa~}l8T$xq> z&~}mrTPD+?Vk8q}%e)`a%^yU>{EWaZV*p=^m7$EQm)KDz8X$zBr@)D2$aWhPkcO=) zK%wJk(kXgOEQ?8HLhzc$-&Fk3(W*p1B__ZC_}Aj40iWjLxpkfK^q8o@q3n_@WQL9C zKqjjXyia)ZBzS2ZTMEwVDakvXj&PNqW`O*CwNA&q3(c|6sj&geepp~91LlQqT?v7c zwTKj^M?Ecu}3-|Rwe(aFCShP3&%*@!6R&v>g((tj~M=W3*17n2zY#+(=)l91M6*bg-(Yas%gey^8*w|nsUygR%q zuoLbhd-!2IMBp#>2Qu*_q82;M@)87kz@O{y%w$Tu6%>w#URvJ<-0dlLOqu@U6gC`o z#e<+P-g6Hgs{Alwb?q9@FFw|LV}F0~=GIoH;Ea`)X*O%3Gb6|)aOwlK42%KPM^J-i z#53wqg!y! zO9>WXA!T1q(^2SvtFboG&J?@Stn;4!sh<PVH%!u5D{u+a2)zMPRTg-=8|a zF{|cJZU1C@L@~3I2g0KWRsPj42G;|nTWT*if+rVNbwgzDytA|3*}nS|Q`SSyQPl*1#%ucysU3jw-(h6yx&EuV&wIv`#O4*OM8JYr76E~UKtTiviF*+eWCLqhv1P?Jvu4GD4J#s+$%23s z36PL*goK1Z_sS@Wvj|`k5a6-n@yt1A&P;a|s`vlbqfgIxJmb=wIaSrw-Bn+G_0?aG z?!%ymlTucUvDSr`|5|>uZQ;v8wqF!Nd_o9u8Km5j0CjvWOD`6MQCfUVd!_y}*D!wd zkGW(vy+2P%lQt7?!sv!BExESNaKnJ@GREKKoc&o=)$7A*{2TW1$Nu6|zbq^Et$;_# zC1s~LNOhbs#)R@O{<(NT0Q-&*?epW|=mIbXO>+cI(|~AOj8o)Xf%4HBFdRl8ct9Qi zSR5Zf4S+K2X8@d6P**ju+HmFYaCM(m{JLQLC!;GLc$G0GrIcJbc^X>6?d`)F;KHrC?ZG^qBx|6Da&Wg%|i`avJ_5~$O!yrf+?ggx?7<_%)QgG(% z3A$*K)pgAW)0sxTIb8h_TG$>u`=QrjoMOkiWZ><$-`;xv_=7(j4u;>BLcn~n@0tSd z1txYFJnKlECqQDhbpY!?>jHKi_@$N=vZjs*^=~qrRZwx&Vqe-@F2DE0wU6z_vgym0 zT*xNe`RmS=$!Pk@bUL|u_wIYG1j#sKswwnzG8;^p8nGtP*c>zCW^Msr18h2J9q9hj6szG zE7yFCW6_5?n%1Sy1B*^UffSTT3j8&Ygjmp>KNl{=abTPQRLl2vn!4s!tI_!N`JZlG zQ@E*)E-eevj@`fPe!LzJe#aQQv|KJ4##m$lP~c7~36Mwv?O1wsU|1$dJW89eIH%XK zECkcRed()M2Agi4g82v@# zfE*+M0~_K)3vsO?9IH657W;e6aJKo(;$Z$G&DvKCuFUD|oxk7vpUug}FZcKN8UTqH zi!vJ{(}h|#nnm_yiJ%BHiPC2~EDRE2Dc09)n4Uwd5$u?$ek~2kK=NTL4_YRaamQ@R^4%>>wvXZU*fe@{>>*&?HfOU*^A6WB|9i3Ya*kYni)yStsEzMc6 zHV`VOz+5jKNLEmPpeZ9IGx&)gC_G_%+W>j@*6!CYY;62|clVysSt18{U}HA-1YsR8 z#Ey-_kp)4Ts_`2zC=-chMhvImVL)9cW|?b0&QIAJzNM7yWb5&2HQ)cn_>-4^VKi5Y z?}F^=HK_k&9ojmVOLtsNH@Q0A=*F@!?o6XOkY3lcS|t>oD!BOP;^~9c;Tsj_vql_A zrbpK$)|QwY$Dyoblf}oWDHNMu$7U|_V|FGwo9J&a-5hJ3rW%GK-9^Vda{**k*Q}Ld z4t0H%uMV59%%;=XQF|mXuxrpE{#0{aFI{5Z8dkF&bYbfvk)P1T+7B8iTp9-yr+|@8 zBI+>`bB3{_rV*3LY{sPgN)1dtucirD)G%_RspMM4O2Exi=}iYpo+fa@GzthZWte8g zp^5D_+b?bKwJN-yQ}wG{H%qvlA3GYE9CR`y84?Ef_YCqU$` zsM=LL(4gQ+Rjvp~W&D_sD{~Y^M8Kz_>{D~hvELWRupG5ECe#$HglD+~`C&C5n=`@U zGIWQ_gMrM*q0`BA2_v@Tx*k8-;=WMW&Qg0F#k%Sw0f?3&V2!qVP)ToO7|xE`Mty&{ zmLRu;5PC+uyU1l2FsPKJ(%kU4oqosN2+s@^ku=!5>%$rLDrVc7k>M1ScU-DStMR-7 zmIGH2{v5^>o&w}n&45)hUV1r&;yqYUyO}tbB!HE0c_3+ zBq)Iz9F_zp6AoBp58Kpg)<)r)ta%;JdG)gF?Gg`GI6v087(AN5-;03m06>&9C}#n- zhqAU2>l!LyP<37Bzz%WsX>qcv!;-moS$cd0#!BR3r{`upCKz|7w7Y!vE1ZMoc)~jMFxnu z@?ooiq*EPQe&D}GSIeeTchh1%BY1xyK4(0cz@QqcT`K9daCeU@Oh-kH*IwAQe8(Fy z!4#1%L+;>jVZ%2+1olsoInX1*9^$qATZZVaHGYY4i>O5<7A z?1{Eoh7nM4J1pleYjQ@-F-V2&(7H7@z6uot*++9(e1)>+hU4K}Oh_~PMOkVZ9_kBx zL$n39AnSPtf1$XeE@mewMH}hp89mwPgz59Wj%XVfYXv$lg4E4~OZ1u9W@+6~NMa0Wyob*@vLUe7~X=7N< zaW}M;fJ&bO+GUPb**X_QFncI&kzx2tRI_luJ{Ce}Uxy+(HmKtTLK2TR@Y!7v$fOFZ zzLCa8@yYPMdtElpT;9gTMMEAU3t7?b!|E<4)6=?Sp=ks#E+VZB7h;h$;7m5$>`Opr zZS1(SFBrN-ei!7y$^wl@133_THYJun)jcbdBxejp(}A8QDI_$j2I{Kv zTCHz^>@RW^YP-=qb!NKM0yD+K)&kl6m6{2;tn{*LyCfps%I6Pr1|NL-A{@uvzr7F3 z_f{|%)TxCN>1uIL(J?!;wU&Z)8Rn*DRhRHbc6f4QmPy=gE%bDjzQf4?_I8#~55i8j zl3h;al@^@!whod&>MVQxGEX*7Bpclq^gVcbqH$!&1y0QgJ$-3cH2ke_t@h}kPix-_ zk_4ljj{v+ohJJU8oT4hd83`+;XCADJ-`p&nc;7TG??R=CY+9} zgEU040EE42imyv}(e=ryt=hs=05}RexX&A4!dy3Hmt!nuXz)hrMu`SttGo>}WGxgj zcCE_3r>Ex3mdkzadlQ?}7XM&m7c@xEl{!)C)o$<8$(SerHK zxuX4Vi9hY|Y<{p-<=k(ZMd}gjIItFZDO^R z>7Fon==l)62bXnP6Kbx3L8bPH*$p9CY)t-w=JFMd71}wmbrOQgXXfettbF%duc`)Yj%8t zF|!JpQ2=Z`K0eg~svVW{yN56s4LWrB;W*!+VFo*$r2tds;<1Hb&SC^;z#!Y}2sJ{J z$Ctq4;NpN(6FaRCFo3gnUUS2D0JYDEEr1%*I7O9qVG%ERV-q(Ah`Bj=K!9SPOTdz$uOtSs z8=i8IP}LRO-Z_MgjT%OyDqCe;E>=4w)t)o6_v}2R zp$>XWd-6W=;9NW_loZ24-PwL5E$0__Z|k|5WsFk?dAkr$+6!(R86*ZX2;ecA0bWRJr`#*H>0~6 z=*0F-hsJ)`iKh4hYgx33&OYI2Bg``d;Z1esSuXP&8J@K1hmaGd^93}u79xfcU}GIt zXHi2yyP#}_Rl<^s+f+tt4b9!-rlcj{jmnWtosq7Pw5wNt= zOIH!FrQntdrF$II52X=X(HlK5&|Zg~nOu+t`aE3714@weW@jlE$_x}jw5qS(Ra@lM zdj@gS7Z_M49afjsWVedPA9q|oV}{QI_X#b)mAPm?$Fp${P|&j;JLs30b7cwfUG)N? ze^gcFA6X{MMf1BcnEvB{jx`!0Ny&if?uUi|DHvFS)*^NOimMEqx>nOf;vbv=zoB0$ zZ(FbfMLE}zITji~73EbN{WwN@t3yWSTLNmdyyOqi`$vZ|C!9Z9$-O1WL0v26-r$nS z>jpS%GNkMk9Srss24cFz?Dco1(x9Vo#)?@+^U3gNJGDlw`V-FKIb8q`9H1{*{1q_>(HtkfK=p3t__f^IKC z4q2858Hd9JPOuX? z{{CSLj912v&2?U(JbKt`f#v=qE$HSO$D8yx4LTq4VTR5#m7JN^jV8@~Ma9#SQTzh} z+iq@dRI7v4&8_j|vv3LKX7tbW&rX>a>H@%(D{K$i`UTOpJCn&!%{Qo@jvM5 z0keKTp$!y#(h@9KzL3dxe)!M_i?bxJl(LylCso@tJ4cMaz~0C9bRKNsIDRv!l7}yR zmc6w)6ki2scV<&15p83ZVPaO|PXLxI&9HRa7m)%K;p17iY@~tn0a!`FhFW2ja%XwC z{ObEIkKeL#Ad2~Ldhy%eA6yxX2Codq{OY}XN3D7@ih2n`cCUf`hk@m2nTE(i-0a;k zA5X*U*3zgfpj~JWNfv}ugMTJ3KCxLXma8`omWSWG^pOkee^tud&~$KL`uzCK#U1|K z!BV`mu`#G-(*b87TFykPlo#u$0S*Ccn>Np}7D)n;0}Mu7aT(WXjbQc{fIW^gGlo+A z!bZC>-Qb%W8`XTidgxNmyEnvmY3NJ!tSZdM-hLX+XI@TGZ2RWn$+HfR>K8GGS!{0MPj(6@N=3!Q;aN001Oe842b8yvY9y7W%)n(#moB zKL_TlEG-78ogqB?FOe|Qlr{hL3qbc@3=04Yw*o-^Px7DP|7QRIL_Qb*;y(}ezrB3$ z|GO%Z5ApxR{}W0niPZxDXgRVHqN*NX(>l}frW&q&b^5+8T~*$t77>4yAmO650?A=? z@JatFC6thoC5BRQz2Y<)O@`GQO;TsoTZnU@i$#TSlQNKru%+VI%rQ6^ORf&wukGkP zZcpAk^Pg;bS9O+JjN~S^vp-(H_MW(3k9w==Efpi#;)X%*L)VnOoyJSIy^;AA-Mp=Y z_4ciy47G3|CJp#nWpAJ=%dqhlQ(LZZU+S4Rvy$1Dhs3_x=9~0KtZ+nAb3SJ(Ia_f2 z*#QsAlNWYB;O2nHMdRPSMH|8+MXF#f~P0$ngpD>v0u5;iM1V zD12Ry_-myH#R_cv1Orq;y}g#MT_mj~1u%%M-71rd|zsWm$b{`+L9qGlf99$!C~Z-bLI zp>I*cVnH2f^r-Kidw+xWPWHafg{P;T?JocL{0aeO z{GEB$LPywIdirZ`e$R;mKel2PR^%v3w%09^kapR$o|_=6Wf#HneDW`MA3*bvrBDy^ zhZlTPUn~ZkF(eNuIA6BGXX^Qv3% zKUOx1WBIAU)X^)z2EuZJa+Z870u}4f^ZRquK5~mqvRDW5Tw)T&GJgI%Azp!R(fh5N zr!!|=3QPCzlL2A9wYAT)B59N~!}~_&@j1FoD!a~v0`3MluKB;LM(dfPrMy{8br!v) z7i#s$d@fu(KZ6e);0rS;9#?nsf2>${w79$aczJ+YuF{0>sc+(~PQ&aEzT&n-G-N9? zSm4kq&keW6u%M}{iFfMOB5TYzeOZ6vCAlfF{4M@mI%t4$XT)B-6fEtS`*jSbd3&=# zSn|5)FYMvE5aYMyE{6@_cdQCS>R>(3uj8(jBCg7$*ePV5*}boP4WNV|^63GG*GuJB zXp;)40X$5y$7?;~NuyYrL4lTeIBcWKGOnYGS_!sS*ZeAgmlqZBS}VKG$AN&Jnvey8 zg?4zf^iJ*^plcTvwGYV-du9tA|6i>OkhCa0 zBaD*GPa`@!ZDan~=Q7VwP2r&b;T34lFLK8qNhw(2G(r=6Y+f|m(u6;46o90LkIQe5 z-EVGm5Q1W3liPq#NP5&LRZ4`S>5=93+`oC&gF%zSWwby8m)mSQaG0-d3~ibZs`HYX zwHujR+0G*+p9RZUy+jtl6JcZ};2gvx5>>}yH9>?5VrS0nrL3k=@9qn%U9$jdho954 zA-*Ww*P?9T@I|B^_F3W)nZvY1)&&f$r}ApkGADo!mmpV)4F5y{PDvU9x{X}}1~y9N z@Kpk%Gv^Phb)`bFjp68oyfga zwiv8Ic#Hn>&}4YN4N1M3XsEpx%c!%D68iq8hXCym`FYQhz1}-8fqdIbQn{za(B>3;`C1(Btj+3j4 zfX!ZDw1T-En&7IyhVESgz0ji)oGsKr#i)*&K$j+V47Rg^$U}v+Iqn@^GvQ|1lNJn~ zW(~mkFTpTd<2;Zh97^>h4|#AU6S4EZfaKNX0%(et*a#da{NHF zuA>M(`@t|ofMxWs-$f<@RXUNfpiuE{tlg0+8`y+45guGheUpy)f5l4abigPV1;k~> zu7`y$x6EFSst1xh-D~EBvjM9=j^-wMKN8GNS%JIgVo%#4lL&@70q{*`1&RS6w$MP1 z^>f@M({ehtUrZe54d4#IgHo#@B;QEi0mr6*Ndj3miCN-3PNJBW+@GNNlL*vZVUvi2 zZR5mI_5k`uI7azOGjtKc?vEpgtn%s?8r@x7`ZKsx$*bEgXQ39>fLJ@7faW7D!WcUI zH_RGP1UK(%H=xB@UXr3S~mpm^3W^jOHB6t(HK$djj$`@h*f8g9&rxacf-S z86CLkkX4%b;tzG~{nRvv@{qg+4b8f6CERervtGyZBbx8loMXAg+kVk4Y;&-P>$ix-k17_L)CtrR$*`$JVB z6HB@sq7QgEQUOXUm#;UC#SZF5l-s7ua=u5Q;Sk2vpF~?0wyteJ)iMPb$FM-&NC$$; z)kGL`>rSx_9xBUDu><-|PO{u%B#`Y`#bkes!m3@7r75bODPkH=6XcMx;899oL6cWN z7JSz_2u)Rj(h^`5xf^) z!QL7A%bmhp0Q#@;H~6y7*GZhwDUwQJgEg_BD;Gr4wC-2=K)(QgjC{3`K%3fX`#r zGf8>a;s0>CWX4mV^G8=r$EEpT@8g9nVdXCr#OZauxLx70pjcmg#Aj!AdOhvUQvsFp zdZ-KV_R2^5hRg+{;=$?SPvAqD9ouOK!pLf^Ixl!+ExN%Ml#P@L`#~qrgubogU5=v( zXSc++VxFVaV}9Y zr~?Y}_t43BteDuyYmG>^(TFSk=7(VY^M6tJ#7aUXH^JClAv)u^#TqO zrKJTCjLCH@lKaZTXhb2VzQ-Q3z*|Z+)j@{%^SNt1) z7N5EmUUbFPjwI&Na7Lo=YLApn$WtwPkY(;>yI%47wVH|#$?NSHl44%$5Yum}J+f8!@~gX`w@4CtWoScCOq z4m~lPEUGV-E+v=E&Y8mMkQkrV<@PmHA4qyvdp$`fXn6HN1urEz~H;xHt zDXHpHtYEI$g4f`L9I!{axmFQ2`l^1IW-W_zOGwJ(sm&d{d3gQ1ZPAT zXS~vg0@2(9D4qV_-`()20`O>(yB^T90W9;Y@0$=5xOW@rV5zM*1`qYL1tw&0LJ(zQ zh|J~)gJU+7~5cYP*S013F49y$XqU;^dHp(pn{=V2PU7%@r2g=9?@I zXYeesmSvje_DAfm2!nqHKL0GTGDypu3+)sZu8>Vq+Cr@sj0Gps@EiOsVxw-drBIyH zvUf&IeVmh{WycU(@nk!<#9*|%2}XLPxdL;U+qi?gg!L;UI$#&T1|&}A^aJo6Fbke> z;FaqTi;z5Q@SyHOr_+zr;18|0q7J#hs#oo4RdIbA{nCGf##NK99jN+BW@%Yc&C$T= zA)#0|TEXw=%AIVAEg2gk%uOw zuWBSU02pGY^>9X&R`OK_grHIMPYag;>4&0&q$Fg6!d}77$BBol+6(_kgXDVhH=@|o zvPv)($N_OQL4*QEvHCA~o%QnZSsA@Nm%<-@X5T@w4EJ{KLZcQK(FR|@oy+bT8duCi zZx8cP8r7%IniN7pK;Fnh(BDEV-G4PapRa7`zJ3*iBnquNCaFavdpN4!p!37&VCfU# zUN5tljs5yVa;|!}E`mL-QQ`CE(^nb#dgM-q7d6l1-N?{e9;0t^Zy#Ix4u4l6zn9(l z_&R^{uTnq3r<|MB_|mUegN4U~;3NB6Dd;`|zR@pB9iNS9g%9wg08Lb(kAlB+@K)Gm z$5cZVPRzzr+Bx6_IVjtKb^Tm=PC z|G~vuOOGSB%Z;FbfZx`@Jx-&K&HTB_u>F2oGQ;47L2l3e?%sIX%uMHow|A18yO3SS zH(29Pm9(fs#A*iWq|Ppyq(!(_=tE)hK5Q(7G0yHkjl1f4mWygRW;ej(h=Ko^U29v{ zhW^@yZG8N1d;f>|VCK)q=}-6k#o6E?ekWMsHo*;K%#Zym;L7dUOxv5?%Y@5v+${mb zrjP>@2nIXz5orSqt;6TSb`V^$L9DhWfK~oPXTX!b9B0PtR2^P4tJ6rTfltS$$R?<6 zzvb-k!IXC-zO2jqQUR`qjz;!5^PJ*D{z5w(&n!mn>+`S*9@7eLcJg^I z_pjeDW44RQR%g*W{{uF%lD{Qt#SDY2Ojx)90M`2d4|EjppDf`2L`OqHMw9;~|G%K4 zfd3ad`riovnK(%I|89W(f1#r*-;H=PEzcdHlYb>umH(k5XaY9-*YMInA43x$Dehkf2a=BNSeablrTwYi$KSNCEVPa$O5Z1e~fqqB= zld!YebL$-E^TX$Ny9*ub8{hK>GxI-+>d$KzT_3%Z+B@skH_kP(?j*KLHPl;`S%biF zQ#P%Rs-<}KJ_i%Oca79&pm*2OIT4NCGtV1$PIh)SjQMeUgWm$(uyZ@}damVC3Ik%Q zttifp;h_H6T#Xo*f9MGAaK$eEYdzy0#ZzkTDF!4Q6HU_Dqb&EU5_d*7PS-xwpfZl<#2=8ed>SKcb{T=jj{DO}96vfdJHNT@Hi7 zc89l}yFFKq>*P_P4nyjK2^fz6n-FK9z`PMsA@`w5l!DHxr2y(0F)MjI=FL}B9(ejJ z`p{n^4bEfMv(iPYw$M=*YlJd{8Jww&oPz_Mq_fK`)bDv&?)S}We!sUWyUsVnKev0z z2x;~U(!a9;BDDmYGTqWx6E!`H?LVsyP;#;vz8}ZW4?l5cNL<{n3EkW5cCLS?%Bg2H z_2UK{M8u8y$t5k(jjEE(0W}qaDJYc6=Le&rRPCDtS2q(c0CTX_H%rYZjAQ>aT?6Qt)hu zQVBd4_8+@XbtV9ko$YN;=Gn`Om3YhMev>FRx%u#(&CurDI%8TVwGjXvLwb}wHNQ*x znnV}U$)*)q|Ih5^d=FmQh9z0GIj|m-{6WwBu_2`%V&aVc5UA~S{{AHwuB+7hR}IBC z1)J{=9U+Bi-J;xZFwqzz5sk>rTa;e!$CDZR8^1pm-tQGhPHS83o+h$1v~LwFPZN98 z^7hK|hI%PJhSrEf|1gjPMXCd&yot(fa6*UoHoXaG?XV~w>fxGt4cyBPNE1|X+xf;3 z&}nu3G`6Tpr#`p+6vX)5%ZUwBqivW_LCp7T#e{Er9J@3d2Y^K*V8DVHAS+qw)6h^%H8)ZyNv^ii{?z0kVaE=-u?@nz9gxLUR%42-Fl9mC z0Mo<##mX8+ObnjL3RWTu%+P-g%$<(nkKiV0&>WUPs2SN8k#*KM%&&|DU^LmopMgFc zT^lohTTD8~3v_b!`MDec$LYWC7WkQ`nT4J4p2LQ?!;cH#!yCqySw(N#{EiFSJ_m+S z3|{)<4gAdFn*4O2HHQf7EFqDKjl5;6osE(VIxIm-v_WDp;NKMLPZUl|RR+v6?eZV( z@hVYuUFx?b{31V*=~^{OC)A~LZx)K9l|U)D9E8FkyM<@1oQEi{qd%p1(MC>JIBMOhMB1u(f)h zm)l+2PaNlb;w;^km}@`VzaIB^_0_2-NqkR6^VX|h zSSCC=q1AL{4c=^4m2JgU*}h?N)vAdyPF4k<$0}BpU+oQo!{C=fx!_?8qb_Kq&Blz* z#O4p9NQMf^{l)ie&$fg}=<0USl*2@dgVn9Auzl1noqeP4VfnuNION~(UEAiL$sfo7 z9ZgDIT(E0b-}%VhMr=_1yB7=v=lI@4sPEiQ%|ub&do0Q`~W(n>#ctwe8gsecI}x zqKF6^IKI5h^>#Mz)aYe=vmm_tj-Ux~7o<#gjfGZgN4)de^V#zm9v^=LD&1p>V%|%i z{p2LN!Y9EGAF9IHZkHWcnmLfiR9TV?oYHG>{k|SL;NMxP8h$(teaMZDhdnY8(3PJK z<{4iC{V6NOANqi$$XDFY6wV9&RVgvHot?k)wUP z_N04=Fo#ZUZwqPaI?+ex7;=R5KXEgaGSyA@HJT;W`;%YAONNCbq$62HB~|krfV9SJduXBP8=q7 z^d|^Rzq>=z+X=bw?~jk|0gE~!eZAkey(YiKaze{EDkQPNobM#tc{vXkYQkN#s9n$l zVnXRKtm6>NLLD;JkM z`_qDC^z#l%n&T)-=-+z}QMgm5jzTPo5dofF=NJrM*yO4Vl;MZF3Rac)#9_>daUu{? zO6p&9qvcYr&s7_#gkz&_O={H+_SyT+EJ~8g*1Vjwf}yJ~5;uab48&8-{UFXW9sP)S zdJu7mmpRZx-ir%aAR-@&#U(5nx}C>FkCctYDYda|kVc0ntx44}6VIi&rAfzfjAZ*Oe@kLVd9xk3KrrYmTUoV{ayf5{Cna2JM>_oM$LZoB1uSf`iF z{WUu2+R7(A_#jf@MFw;%((ll8C|==H|13F|COkr@)qM*&ig=@U-QZI`*j4XHZw#Xh z1NLPRsbgtWu@Y`9vM7>i|7fbKz6i807{-LLxt+`jzV|?G_sL}IHRI89okRg82^@Ns z>s*+oGgmZwc@=)@hEVj=lVim+E z_pY3UJtQNwf$%Aa`k`pgw6*ppsQ`nNG`1&7iKE!^JJ4}88>V(Nf9y7_5BK6p1waPI znZFuZN{l>!83x+Pk>L=NnO)+iA_slfgv~{<6VR*yzb)8(4D-8!Pu^}a_dkc8_d@gZ zd^*FqgCDv}WuGYC)XN|)_-!1|<9L9`7t%r7O&%OSh4*5OHSwV6d#B^EQ4zmZB-z@Q zGJa=c>$!uW*g7?qVeivq`m%7Z4TtcLk*RqN>~?( zb|@_(@mD-_+39<`n4F#$bQ{vjy|&58$%*Z89!*90G<1-Zs^whJy&ZFM8|{9 zA;2DM4iMogsadAdcQDGT6UlEm;xbwdjU*LvfMz)-!K!W>=8|yx)bleY)ci+u98_YL zkNl5g?lVmWkTxsa4fpq7BynV}5t&~O@Wc#4a*^24^B++tr~Ug`lr#^X{atqnOIOG#hit^b#Aq{ij;WM}JDnyn*l`+ZwaNG!? z8JnSsZHFM7rXbD?Y+NZerpbX3+%HDv@mUut1HTr?nq0^RkA4^u#;v${Y_=+e>7d=G zdG5;56=;A#WO5`s{U%lRo-|J2YB4!G;7b~9cpF~6;}*Qpc%+@pew9|>Kt!Q$e+XWn zctw&tGQ|r*7&MU=^d>#t$h@tnjALXuZ+j|iD0-?ZNu01D5dteQtYEJ1@uN66N#FKV zA!Tm}{^!{)xuH+PJb6{igl73FQbeMzFKd0cJ~1Pb{=S%%TcgSxucu(YIxhTh+hMk8 z0r#371K=?u-b}2~C1x4S0hJ9^&I6VRL%0~Ti(^a_F%-_kbe#KaN{CuY{#Txnn6=VB z$e=kId<=ruEPZ$?4wNV=>f=BliyAqmwTBk1GDn`YM!3=b!!ZcBw`MFp3(THzkd&+*_G+AM9lPEFspJcing0OP+}D94iO== z?GHVi&2SG5?7jjPvusx9(J1I83(9va!4OY? zl$0?giCu1JirgW#(Bw>iv~DvG#S6ksjS#8qCmEp_AyZniF$zOfQdAM8I40d+57#9{ zP-!wKG$3*y7H;9_hgjG|<40I+76LF9ZAe?uw>~f}lza|xrcYI{0Z1Il_^sWs zYRMNakj1;_3~^LqgR0km1*)%89Z2wFrBim(v87vJDMQXZ_iGIx0T9DI3ddYIXI`mU z*+RcYLp3w6GR@ec;CZk#peq$5vE=v77_NbbjHOxtM3&@0Z>x7#k6-GuBzc$Xy%PAi z=^~?tipZgMX2IQX^HxfD5o}zuV=tPxyQ1{D90kKpH2D*kJmdMH5JXP+N3;}xhPRw$ngG7+r^}VQ3VXLmcFk&(R zbiqhTu1|g_;2_1HnGTqYMAxozmm>kQP-2JL=4zv|s zPRadHjRMI<7wOKpOer@0oJOiq7s5|M5I0J9p-e;+5)x&uE5?(^^xMCJqSuonvy<$5 zO~~fPOrf1L=rqD5tYV1spx_sat|hdTa~=_~OMhOVW@d>HktcEuPM9Q~v#^)0wl~LH zWk0TtDRNv(qvJpa4fXP3Xkc>E*?f2h63_gO{WMYjO=4O3~+_ci-z`E`vIFFWF{Ku5Z)(n`?sz=!R4Dj45P@73+7K-X^18j=w(`Op$#&jvb z9OuqpYT#(F{(*}rH3^%*sne>%8UfFj3{0n@l>GfuClM=m8K~c2r*wF2)a{;S1ZkqW z^6>~VqCrtzR|vSN+?VEiIS}-SC4wOlvZowCxL`|*Ab39d)GU;&dt}u`i`GaKQx^-g zV)?@S=LYzLu(>If#yPa|WSrN)`v*%{ARa^9uf1e~9GBZ$kF!xG11xXBv7hVI<2{W} z3z3rN02(y#cj+SR^q`-J!bEH@se!eyCRX;($=vT))u6C7R>5xUSdf13L@v(HW0vwV$X~1V+yJMDfW*D7q7E zfAN?g=zG?I-5Pu*b5^=XSx_P{s~p<_}W{1VG$Dp_;saZX`=BeWqB4pvuHTGC2q5377lgJ<~_{&MW`N zAlLQqE#vbx{@8%vzG4HEMsQUp(0H`$4|S8Bg?rb=_S$k;z-wtN4}rQz^s6ftr_fUn z^TBVv-9Z$vTDo0rSl!>8}m)CAvaY9kdOc_xw3$TuhpUY94t0)IwjN}20c z)p7&eD(kk}o^4G6Mg-9J*3FBQxsL}~G0i){e>f{h^HN+@CmSrZkP3mj!W;e83_-<} zxkyDS7#n|^2+|`T$S^4W_Hpm4-`( zKMDy69c*C@4G>o#2jA1DTJ8^QAMpW{6cW#Xcj%gENnj6SjTil!j>3svOTzRq+Arqn zupV|anlNPiEo@`(R~bAbXDPGyXTx3t8>2i!03gZ$AfYICkpLW@L+o4@Af>7SxtIb~8>P?8r91)7{)^M<+zyTNo&{#DN3`QZ4TO&|ZPw3m%+soyoes-4fwmu&K;u082Z= zoxod)F?6_XcPEK{zw0I9zWs-^c6-PObdEph89jYv6MdwPRim)kl!2UK!A`1Ahn`8| z>&A_6r)a`Tl$L9Z9S8GY17r2wgzyN;RE}W)QNEp((iYxOy#1Xp<8f8!C2q<& z_i#}?C^B;Gk2?ucv#)6x4*Gedb%Bkq%#o-#`1%KtC_L%$At!mN$rz&*Z%D5pHj6HN zS;;V^{00m*6%qofb)wyM1q7^Bg1k7gOiXMJ#pns7H70gXid_E(boF+%Cd5@HcNSqP z9Bt9MYeX+^qt$gs@tyrnK#^Otpb$5PvAm8&r>?e*z;`07D+SX1 zVo^0lGGkZxkz4xX%{70b(EP<&8pG$)lTB|Q%V?VNMBHVvN*>})ecC{-JW(^m4z@fA ze~af#c|TG$dpeN4PRf8vofYlZLJ<5jfUGLwr!K4bMH_fTp1SgFq>-2D!SSSrqgV&p zlrw|IsF&~tTOE&{?@j_PvdGPkcK=GkA^Xp2?3YR19&UGZZJ#W0Txz6{NQ{Ldyxgq- zS}Se}m2_hkTFjOZ#1`~bu_AjTFDar=SJ{q_0Iou1@Jz~Gz$4Y%Y|=y4KoQ?80PG2GI_FX{>vYeak^A@n2w z1})tMB|sJ#VP&*F3`ZR0#;t={RMrt8Wt&u-X&4-S9xoR}R^3s-=lR<9cT=#*yMB>q zx9B9?-yI^FuBVD8GsLteSiBUBeXtoKGR7LZ-&zu_$w`Y<(7OZ15`iS{jxK!eh>&=T zZ|A=AcftoG;*3*fih;@MZK9*mcRm~f$JR)mIdF9DRT9%PFVB%OF!`V1WpYWPm27!v zhr_+;UZMU@I->jtwfGVGc zbkGph+bvt{dBee@fb2qXEn#eqG^8T%DRs0wZNve|P6UfJSSAk!pg%H)7{aiZ(r&yi z0_5c^n!`_Zs2`K+=S?;)+bFMtpAG0)S}V1{leLIE`QZL}sYa;Qjx5G@hdC)2Ajfk| zZ8alGZ%yhxJ4mJi-8D%LTBHMM35+3?6bN3-k;GkhLp!zH%ev5J9;t!`e@dNJbWq2Z=PI!|5N zFR(L|W?Ge(1i-4qAfl~1Ag>UrZ(R|LZY_}zyXtJTD~!;Nk!TK>FE zG(p`%NduHPD~nrbEiVqbgD=#?HE!aj0^?Kf9F%?1Dcs&B^8a`yzoS;8Zb=WS#OGh_ zr;ld%up~>O`%8GSELe8;IcTZhn(c1st=@~5^u&9HN50U)+$RvdPO;2buTh56CC;BV zK*+@Xm={}D=_QA?&{rjg{ug9|?{}<1+73y*b}YAzLxuy+YJ?6MLSgG|fdkwbe455_ z817op3aXHZ4dn`)G{Xs4-$B_u)s?Oq)6%Qv<%)w?Lo76c;F36$kl4XM)QX*WDA`R8 zNLt`GIYuH-+aq9-FDg@DJEvS4Ym=}1Zz_9%lXo&aR75PVqz)aLY*S$Xe=WsszE<`! zSsp)~do1SoM&GIks!-+$wW`K{!aRVfyzW6gv>+~eAq}1Zmm8AY&eG(In>-TjBBjR4 zcLRqEh64BOmxZLbRuDwp_G8W$%sP{BIJS*Zca0gGYq5ph5s|n-pPO;iMZz2pm=VF8h5YZIXm zL9Wv_!#ECpLo}==&-30mD8QFog(K})3DvRsKZhwt85aBK{qnp##KmpCYi0#B^Bm0C z>mABP7@)+0}Uy&U|}!yfr2K9&yu^;9|DxZLo}VJ=$0Ny zYF=Z1wtL!~+&x;oeYq2OCseBc+T_|G^WdjPg%8pcr!1Dr$AkTlz#ypO@fe5<{%YC5 z39JDA?;k({F3*el&fUIvvKboeNVWz?da@lx?phO;@I>B#2|Mg1AX5Nl@;KC8KUAA# zK004$PHIzCDyrg8NWm`mw)Dc_-8Rh-an%gNeenYApX22xaR6DEr=_X4q5`1;yTT{x z!$wkN$Yg%qMW6w}zz+y5Zw#JhTwV3XHqrMOoKR&JQZao5OI9wq%gs8>u|sW1yTxP7 ziRDnMw;Tp=rn1DsE&(s5Ous!!lKUA#3Y;V)_lR@eHUNnweIJQmQ!(^^4-pCG%g*xT zm@?AIW)HNQc}D}N>6$BTNydSvi=(89u*L4(P-dQQROc{e0<$VU>2v1%5<8GGs<6+S6>bc*XeQGG7xgF1n? zAhs2Y)+8bk>Sc3{S+>Is`F0%pa@blO?w`3SP8pAIwjb75`ON@7cP0Vb^1^9F@E;(| z6#t8j|2PxF}L6hfnEND9)zmlb|ko!eO3nuct@2|6e_Sc8@VxH;=H>2flzU z9>>2bK|*^>&f8>68bZ@Cuz#^Bd9@%Vl9f-Dh)pqEp|tp{K42CL+w4;pV@AO7GM8fO7&@AG$m?DvFU z6TjK?FCDPT)n2eUqA9)9F`d;k_yY5KZA?E=1^&MqmR8l zkJngLD3;uV$xL@UFcp&&`F1JL&8tD7*Q;kznIk)m49URP?Ex#^r@v;<^@JpK8}~gb zZ?Ts$@+->%g$6oN9~u*IpxCivz=)VBz$ntLC|#8QoQ5LO7?+fFjlYCr!QS`PE4>yC z5@nu!-(34cKa?0^&-#r22T@>m#sqceQz|>BSUbvUFQJ2y2Q{497iw}1V`oGAn?@aV zNlnrU32z?rGiWTt(?A!A13kcGxquYNjXJE{UQ&j;%wrZknz^a!$zXQM#gI5IGc`+r zEQ#}G5b6tgfK6DLYa1x3T3UK^D-?m>#m#*E_R7=!J||%-2^4N>D%{>TyO?^SZevW?B^v>LH~0^vm7O;g7f>nVgA0;em>^T zTkNvU&=W-?tjV;0LykqCkmFEl^(vI$Dt5>Tcu{Gf*_w~H-ya%!!W-1*0G~B)k`wvS z`%6~k!*7ph)abQv%`zf;&gQa7UaJ~)0S-o6c#Zw4_4XkC#an4hj(Ka4#A25p@d*J? zUlJ)00W%H3?w%1LzjBV1brk{4ERqxt`rsGkf5}*_vHei3{?IN@?%Xc-dqT7PA^YIe(xb_It>K@7;ZE-!8p_k&7k9Y^Q61~l1udk4X+Fg3%dnhy*RAO`m11wRkEJ#^ReQq=X~TVv8`X|{Gg z3RcY+r_YLKbV{3ILQN=C%a7%8I_N)_^F+o>7v`dImoc5uk_W36eM%9ez$<*0!5D?0 zC}OTq#l)q33qzZIrp++fJk~+eFdQUdEcOAxy5Q-Y7|&wfx7HbPVQlK+UVrIov4bKD zDMo12LRxhqIFoCWpdh6t5Jy(P!IU9j$Sbq02CI5DNg}#minM1GZ_5LthpckW)efKt7u7|lb{Za9Lo1UD z$k`jqg}|lnz2FJ?7(_do&;GH5UglKlX%;b$Khct=aQd+Iu6$Y98RklGbKr#Tg({!h z-9kqckD!+z+Ag^eDubiwM1YSD!9@3|WhP|@?keJVJA8U5M|{ew$a@gHJg>5CT{9OY z@&4oYnNO=XnHKMtJVDmLJpYmuZt5bRwaf{;r#3lvnw_|=F0?$29-t8vtFW=yFMuUP zyM^|`XlPHVC9p$e+9+`x{wLRD1_&jlsrMK&%FuM`*t!lG4ZFX3M`j_~OlMk7DZG6@ zqrda_7f%9EF@4`eMmhsHvp2@8R$~-{gD79C%*YiP#M#wweVHSdp@HtuCI8?PFN zD@vLAkq(n22GBd(_hJUKs&zwLq(()quv;lsu;T~!fv~V6_|Vb z)0XFpu{}{SJ&Yg?NqfJkfhEN8jj%i&Pcjz#z7xS7b50$*E(Ce5Eet;gK??#Tvdf_* zE@XHVxK(#Q!7>jOt)s*$@`?}sAoS&hJlE>?FZn+S?5F`lM!FrGZ1saI&G?LrEuuwm zpd25m0-P2d3#S|2F@&j2tb$y08?ESfTa6aM(U~pP#^x1Ehw_JlwU$oh*zK$;X zl@MHXxE;*oR#~WnZuOe%76vibr^WEVwaNWNec!q5e`2PYO_KTskfa5_%x0E)RuS@3 zn?P!})~@jc4)a6ZN>CoYrmokUyz*lYS@v&h2+Y5ge6pL9n6IymWj=lg#|TlAE-JnU zImF;3Cly%mviMzyMD=mD1(-`9g`^-C>df6KE)t<50sp?v#UjB4S{Mv*Oo~>w$%iv&jD=cz#rHrrCjMc#RE#6w^+6ITKO4WEnK?Y3 z-dK5ZH}3=DyO+0w$3cM@axwHf7Jm7kr9JhzJF&e^fBJ4RuF@Gv`kFbc&wf z1j&adxSU2DQPh{bkXdNKl1g>u2P^zP9p45!wT9moHdh;d0Y~?8F|%uvC;WCg@YG^0 z-FLsb!R+G|x;`RnyM}}amN#~A9ozJ+kSpBl1Oh#U>%ZRm#Qm|mmlDf8G4Tow!?@w? z>f~6D{2dCjjZpFoTPlt%A7X%zBq_0N>Vya9JAh_z!-2nb)vVAk4Fhp|H=HYUH~?Xl zSdk~%6CiKzfp%*cE7VQ3Ah}>6U*Zvf82w#3%4E~$^f+Qc;=A!$HgGT5i}5ezM6~%2 zjA8}g_sc(!{j@O4|#?KF@?Ug7OSiyp?6|dli;=se`iOd`c!Cy7VpV_U9Kyx{)omtBq*yV|9f~JH8 zI(?76^*=9A^gCUw{9f;l%B`|y*0Vbh`mu$lShYUegbn=A?a6&EkEhkR4W`mC1d5U! zaJiTvY*+&)MwEhfcO{SIm5%v?E-@vtblqXkJE9*N>~!|%{F_C&1a(rndO}=cRxe-$ z%$IRGWB&3aTGMpk|2S~&yxOWw*+H2t^Wxv&XWs<5w%Koe{S&v}dT*nhI?R|%qnQPu?l`RLH##D`%fABAW< z7A_DBt|{2qSbxLD=~n|6Md-C2%T-o%r<^u>!jbgBDHgNk~|p!qGTvi~y0vpJ%2&zkeI6VqJloQ%3V9KQaCaD!4g4emIyh5bk+u zXzlvgZ|&C18EUU~`*ZO1xwKSgRxFf26LWK98^P_k`)vGYpx*(>Y3mkxeN0t>jylLt zeEl`AciynVq^ls!;ijkEtUg@d`UwLDHiBhp1{|9-%3%IW}_uhz)HRu z)pvQ3LFhf=m?s`^!){ z=5L+jRX~Y#D^}ojU-&M4`|ci@Q=wLIN8Q^yVt9+rDQ>&4q;v%o=l#_==O3#{{2av|%a+8)TXdlX4hmRB&}}bonO`@&0JQE%jJp zEXd)a$&>?b-p(6&VD#tJ7%FEg;e30X(faPm^V^)g!%c1{zo)}awu05p5OWe3L#XAU z{Xw|SaM3;}ge{EW$+qt9P}S~sZ}6x8nh z|Dh!QFR#JM4gdsq{=dA&|5!U90RNlU(08e@{GYe~52_Cc!2jhn{^tt-fYmvH%s;0H z;Q#O%m)&x4b~+x!I9W+qH)?fNIa}w)<-?k-W1qM5dd21;Se)kQ`O4|?Nzfc@Fso1r zKY-9sTq2>c0x1?E0Js)_@AshMU+ec_WdAWKAdmks&!YZq$!(FW!1b>7U)8l%-8M5j zW!K7QUiZw&HepiIjau4EcT}UWZC&?F`$?`ZB?bGv^-~DR#?`a+6Png&b%7!AoU=-X z_ZRRc(A?kceTUD`AYkX>J^rdQQ)?INJJ9+P>K6sjr}%Iq|=A{wNkUP8Z2Qo3*9)ga;7$WIg=!Rq!9HEef`T49{d^PuCMO^*@$!|0{QD~*P`G)G=wmoG_JMTE zt|ic+bIT9yP1xoA`O1tBp?b>W-(SW>uloolBUz>#?YS@hDO=7>`_Y6%MxV{!xG>_a z|KsiN;&N>pD$(0*Z#?<`{exrf*Xi5Qk&?clA^7#k@?o&98?;V4@^SX| zc7C={UhQgw5D78dCkl)9g1`?y_KR)hBohK)aF&@bU~t}%2>V;>gE#lx;D@~V_yY4Qm z`+?yz+%9w7pd?)vHNlN^ix2XXK?oUjRt~V zFAyFhNOW=k_>HucM!si(_s$4TFIJ8ZJEtX{FbPb-$%6(aY-zUe@RZA-YVy_Yk)ICj z=$cw;325JSWjycT{yW)MUH2->(tXV%z@hLp6&`jH$Wl($M3Z%f4vOYn8zZi zrS7L^y^N+;?nldU0yKh-y;B6zX%Ro|^o+uzG0NtW6n5e(w+M*l{|RA7W~gkzk!N}C;%4{Z!X=BIF#$aLY&%Zv zNdg|OCOB(?pU%d1I=lQ13i7|E3&6i?QW$ISNq20q$J@IjzIWv85reJ{xLXRg9P&R` z9c8NVe8C;k1cljQ<<%t+Q) z!6krJdBXL8g(A5kQJ$9*bzDXw5>WQ2DL!OK2Gc%bh?IE#n+z;E)jgqg+(?-%a$gF?Vo=DC}ptnCMv5!uvSY%R#S76Q8E1}ZY!qm|* zuA-8X(fe_0Pc%P-j}}l!KTkjsN(le)GB(rSq6bxyDm+;Iy84C<@q3TpIGH&-nlp)9xZ(x!f5EZOuZn+ z#1${|cgvGa!X;Yt0*0{v{2ETk$}8qh$Dg{NZRgk0wh5#1)PhC3!yz=uy$^Y*s~4yel5w z!A8r!OFCB3M{ti?V9*AWbKUVv0TMG=E^UIhX5NCk$M>b|q^aEd(y#QBr)XX7?7Q&> z*`ppY#M!+GDYSEkB&`s{^8|2rkHwtY@JH@$KR+hqbm=cr8hc&u!X3e&lCKxE9yr^x z4nxeQlbQ4NlG41GcPSXh>#w^`VQ-(V&(f5X1>pQZZ(-}BvlPqUladChRuq|-n0kH+Z-HD} z15Y~x(w1?VR>8@9^+q!Cm30GQ`}Mc|ch_2^ANymVL*YR1A!~psnwCEp?{N3;AP+#$ z%2mUxbYwpQiVx%9E-njDMSr4v@FVVOU)~vk6#E-YwXBm|jd|f$G+Kg9QDg5PXYh8c z(Y-86y70#2D9p1>Q@B#k_FT6fzOi5rKK!)eb;6(m6AMbYQwscRe`{l7hZ(3#N zoO0Xa#ufd{8F>6cU>m?#tVbGSbE`|^$HN!keEE8r2W6ogH=@8)A9+)xgUN3X={MZ_ zU{A^?6!>_nr6}C?222ooZiw74D&u@%e|xcr^U2_{+M!ZyXn7?Ce}>-Z?+Cr6>9sn1 ztmZiOy8rAIah|6UUW8v6HpF#Ii=2Oa*VrrO)5+$eVHICKMEM6x{?aKMUaHA3s}fN~ z()9lph9tvdPA(RH!Sop<=Y76vDfSnPnPWfi*znDvhLIyg@6Tya_psJWS2ZytJA))7({!tv{Kqd=g;9Sl5s z`UG~rEjQ|dd4Arp&P~vFI3eyI#JCYSG&7q$y!G9Nr!?wFle>4Ds^L=xG@R(FlF^Ix z&f^H-flp?Zh{qZQ2<1Hz^wF?SwUWbywfwZrU zhZLFXlt|{o(}X0y8e%(JzwSx`-wQs=EapT(qHD3y#+jvT&5ie?8l4&my-9v;8cIXNO)qBN$$; z)lh5tQh9kfrNXcj8N-c^CyrPHoCa1`LYDaqF48tv16}2c3A3LBm&x>a>X!*QK{N$< zv>4y1^04jT0@_X?yAP}kFUut848heUKw+=jX^X%+Gig6d7_YX>X$YG2|UD-3J~c1NHaoN3>rO-J&bT{jnv-@ zuTFI&5|5;;_|;{6G8kUp5FLL5O(M|tyZUnUCO7{RRKmQP*T;P0PQ#KSy0+v$Gc_H1FPDc)!`^8RLZ=o ziBQlsadG=lt9r7s#aA=ICoH%3;8S_4Z_ z5F>unEVXn9h~B?lQqbHScdS z@ck}16HyIRoiJW-Pi3TXjzGzJ$c$c&VRz)D0bM(053fPBn!mMFkUal2$MYtMZ4>*% zej8 z_YR@5WO~GneQdGvoIpm6Qx8l7yf#=r1b?V z*#Qk24Z-+4z0EKO2m_iYNuI^v96$O$dVOxLw~Pkug|9gBX5QpX8h<079^igELI4q6 z3#Ha8WtXys(fiki&~dtt`ZK{RsX*n%5%Zs*+%)+D%;ZFRN|b$_Cw`-J+$R1@B6_Ig z{Rx#+ig2Q_Q0rphb95oCd~qqJF|qoX%4{9*Xy8&GQ&i6?-E|u?eneW|(B%c&A#DVm zORDa`^eOgg`Z`}+cc;(*Nxqsl0-w#kCZ00_vjUV)tzP-L7;0C1K>1i)x~FYdJ3^rI z5@FZ&txu!Jm?VOkFNb`XAPs9~K)_v&flkH$9gZr|{O3DWt~O-31LBik=uY=1&MI3q`;Flbpnj?>b{eW8JVihLgB< zmC~zc5U9I}l)^)!A1#tqe=)uV6k-o zdIb089!C5Os?57xg@PQF&E#Cp2scid>0&t?UBA3U_274n%-L?Exc-z-om2^CBtyUc z8yTu;?(mqAgT?C)(XNGPWl$Na{XF%qw}luYUF>M}!PB7Uwb7=}Q*$r)u=VrQaqk20 z-yQ+3J87hK*nhzrj{XOCFS9`6kDW)`XwNh5@@ ziu&2mrxI`}eB6&Il&|yJu$`gFQ`E}Gw_R~*fa51^+}vdvYp3=UyHX}q$cTfGX~iY~ zIJ!XH@BQ5SF8LhOE=SDG&=iTb ze{DT``F98k83=c65`FQv2JV)8MZ88b!<2czLEE$hjB(u-&zd zH?~*?-)YI545}g2t?#+k5jwbbyk7G|Y3en*x>AIfUJ@K`)>n_gXbLTNRh~ z8V;9nNS3>INPdq~nQ(e%&xJ&0l=yFR1R3TQ?rp>AJZ{;2d7mhk1QiRbR!6@dze}$s zhMS^zhnb0SZ_qh6)E-p&GRw#B-RcXPlgG+Fv|Y8}&pI?DB&#HvkKlrmr3}sToeuvm zquji0P){U~WdGb~i}A1)E=G2;jk>6WeR`^Kz`^+dj1}!6M=maBcI*^ygwntLIOfkZ z)IRxir;g-sBe*fN*VRA514LS2TSY#&)84v{%-E;f0%Ky`K{C9aE`(p`t7+FX4^Y%G z@iXb2E0Z*YtqW=ku8Y~nWFXr>C9=ci<^3*axGuK|nT*D}kUYZ4Q zKZ*ME+wT4MGz)Zis$AlQnIDDB*7Q|ow5i2me&D{@^nK+%bG-Ba>O|Wa&hXM~o7XtP z=fKyJ{cjIn%U6%s%NLvZ^@IfGgH85|htupF9XC6Hixq)2L5@YW%o};@USYKPRlp)1 z_pQ8sEyJ}6m_5{QdB;jZu|w6MjM@3y!yiG}C6Sm(kVk5EO}=B-7S5Z$OuZZfO&4_T z{5iih3M6%K=mnj>6chx0IGQS8IX5TVt#}UnY(=R+*g154*$3}C3j0Y1W+O@UG2pr~ zj}YN=PodfMhq4QmnY}!wyZK`3lQSGCj(?Lb@U-3n>Xw7(7QC*-?Y^aUR0=sDGTp(F zZiHT|*AGYXlO=|J;wsLes9wf!MmF!-qjGxqLr!-|l0m%c`8>8Fv_76Ze{y}*2K{%1 zoy-M5JRt>npU~fcFX0S*udslpXdbiobu3s*+MmN$s!Y3xID*pM+POC$kPK!^H+Q+c zq?}D2ryhL51j3{XGV0&bc*=G!Z6Sz^-U}BrNVN7IXXyiJgM%J>7yHD|ag6#CzWZ0Q zxeQJ>C3T{Wtow@w_JF6mSA?8Dm$d@Z-JnaKh`ud$uLU^!UhJDbJOD`rx96=si`b>v z3a~2(v|z=%?z{MLy+Ha&FWo<0M$i3uaXyMF6n$Xtcr4UL8kr$OGNh zFEkm*uA#IT@b07Lycz$ptkV~F%?u?HN9L9cVPLaD!|T4GYsOa4ryuV2+s?f0SAo~l zc6GrBVIcJ8L1ci-Q9i9k9_zpQuRpkCCZ#VeWYvK5+b~iQ6LIwggU2rvBxfWO{+Ei0 z4Ni{}teFPHX{1fWU|=2Sd*M>a8uZ_#oVu*uA;K9y{wVy+G{=w3<@>zO7om^CdqUx0 z&V*)%b};I$Yr#8FU@3R^^A|7ZLEP)gXgkjQ(G7<(*D>QBzLq=jgQ#y*ij>nn{*GEn zO1}=5^U_Ctl!17rw9UbNfyzw`G(uZUYgC|c4EL5E5g}o?jf}iGS0HT`l0@WHt8=Iq z=ws&S`uTX0IKIqhz76JL>y_Z~9fks08pu1((TZWTA6@yLb?|ctNG%qHS zdvEby8s{rIoPzD5A%IJNoOC;g9790dCHWLP`k7<0QT~?y(c$Icp`%mBiqT0k@ci*& zwS{H}eE+diT!#m`JdgsPbl{UUGiD{?V|8}ZOA*o|Pk2*&#<*NBL+fZf&9}j7;nOW4 z{t(79!?9nm|8+~N^pMRZ;-$jR#ABm4T2ZL5i7o5T7$HSBQo%qe9rLaX)lGlx^L(_j zz3+;OaHPn=^Yo_8()&GWUHsS>lXx`ZIeKo%QrS=#s{~sjcnI6vl46S;uD}VhNOFW% zWU3g+>H^DfI;1OSqxG6-!RQ<`aPR-c+GftGpQB8!9L-AhCGGOaZEL%Xess|dT1$v|Y2|_7|Wz;Kuqa}Q|&QtNsc+$Bb z;{^Yu{Z>vrpp>ge7GXu=Ob8#f>(!>J|Km+&0E@M+s4W6nq%76yXOA-6RZ@JtIGimCC%*>8=2jv>*PmPhA=*FBL$x8-8KFcx_VHtWb7SeG-v*~WoG>tHG zX9R}V)J@>}?s`U?Far{S zon7r);#8!{!W+)ERU|FXsOh0ADKl%;Z_jaH;ev+Fvx0Ez=|;=^H(eLMjgXop_KK}~ zvD=)L9)!YANVMUpqzfguc2ii zEGo%yh>g}iS8Bi8zs+d|V?@NU<+qudYwEsWT*78}0uMRL_cM)uSN!+J*VGZC?u8OJ zFc6!%XWfwtS6J5Q397qG2EpEx)XMV-d%Qy6y~2gyJ;`P1<{i~c!e~2|uz#@vRm#E1 zl&1YW)8NS<#@XSUjX}FhH|n_~;Q2Zu?C%5dmC*V{^~-3y)I9DLWRR6`F>3z& zpTeDmd5ui1;JzpGAI0v-&;L>){C;kB-(p@i7H4MGcCdNJJX-`om$4t~KYd#!?x-lj zC5-LC$gEr7LLw!EK4SZ!Ro%7dMQWnO4&Bl@{S&}dsS#C%wM2(6hR@#oR z>9g!0lbAI1T%~S}=}Q;nA|`97!i@KwPEelk=k1+G_!qZ{ciFJc2?^Nb;2U(dsLn1Z9$C zCk+NRqvL;f<+vxW8zH1ZqwD3pS1)UNR;|l!{C&O1>aT1X4a{j}`{jO=pOu-kJ5e*2 zQ3g0hDCHOey&g9qqQ>aml!7_@jV!z_DFV%|Xo6u)tz-KPPqeFM1EvUEdWoBIf6PqA zk>@0)WB*IWxFO;avYC}U|I3ABV*?HIQ>}nU{b0%QE|;~<$7k$kSJSfh)V~9-=U2an z$At^duWhBZu*|(-pQ?AQs#pX1<*m|E0XgSREvL+=JL$FWTh(x4>$^^K;q}cpIZusx zQYh7rmdTm{7bS;eG#|{~*q5v=i3liq^>C%H$ZD<2W&K%5U(jMe65kbI?7NubhSZoP^QPxkCcL7J()o0NKp|)O&c1?eeZ&2h z!ZMYXi(j?u_-zxkX=)}Qllx^cKD?5_?-$84nk+f|fRNKMA1+Tzas^GG+P~z#nm@1b zb<^Cno1qX}Qnn^ixvn&{A34L%iH2okrU8zk2p!f-JIc&(kgRNkcM zP_0Yn6of_XMj)!PFe~}K6h*m@sO?vy8Yb>QP^KZOUswQFrU_0!w>^`)Os#&8#G|)4 z>CO9hHk7)ga1T1JK2+Lger-1Sx6Yp0hTEhjE#@qbe158P+USr}-J9X>gn37N)TaRv zp;IY|HIIS-vJN^#ub?NmEN`p^E)b9Qe&%2CIzrefMv7ldZKusplh+|hLb$|<#}%8U zbr|i#H&DvGEu{TG$iUO&Yk)@}0y;vS+P{uvM#H%;T5$`bgBTe3MJM7JU62h2T zU^1o%daNTGZ~#PErD%gFwbvYWDQBswp3zpUhrIr#P?!K!x#~sEp0&c6$o9av_8(YGkW7=W3LeyY)xLo9`kL--r3?3@(_-YpPC2To|*dTdeg9R#^F8 z?I6VJ-rnhS88YQP{%h>kZ zg+YNQ|CZpDL?1i54<;(9hfpF~-5W(XsrkO$@FP&ZaeZ`psM9l+Q>e3&u z>2$eOR|9Zu#uF4wT+UjQ1_o!P^XP+3wGY04N5-EoimF^ehVz4+%ZtDwJYT}sA{6PZ z9B%h~-p$m&ajlbJlG}$8tiEVlb1h{Tno_`g$cjXM;cw`;#cX>Ihd11H|J=0iPHUor zz=4OPdfj9P*xA)9yoXSyee2Em{&A|$@slc~Jw!vhkKvul%5`)B=b-%?I1?x@B_bXz zlT6V9&@vzs_vf5^#9O^Zl#2fZR>FG%VmfwRCJD!?fKt6T>C&;5o+orLOB*@FB5)|Y zTDO}6o~08ZDBkoN4LG}0_jcr(5JJ?yf)kbtuVlQ1N-3|<_z!scPzZOGO6$`u#pj$} zco%=vNPQncT{+5A!weNXA?&`4t1}bYqF1UF-7LSGxzGBAJ<>Kn#6+FM(=~`8&qfd23D?IJi7#=!c1v4L7LQ>73C+7C3@&!nkEo|7 z_%U$_v&4I#OYTOlpr(%3|! z4KeVV#yyws!72@D5TAt4G{_ozFKLd*d;8qsnAtz50ulxS{{ae#WNEfXmvc-fq9gOo zmFj90!AcWtx^=LdYNgZ#I7xO$*r+)PX$?6e+GQTO8*Mmdb74z*Pa(8iVtew(D^IuP zxhwix$4HSwuGm3-g^GfFtUU;Lu&#Kt1~*8G!r^Y1xViu28k?Z7WYE4fCg19{fmlWP zkeZY9fhQp8%jYQZLpB;D;DY%sB%_6LJk2TZtdw*`sO1`B)hBW6QhdR4s-a&h%YaYM z&)!hOgQvHc%&_YB6D9{RR@kEI*dXy|m>Eu_BA1m!(#g%60oM8XN54ay)_uMJJdJqv>uJSW$r^K%>ng#+YYGPI57wbjb!;_sv)n^$TMA zcZZWs$JZhySp!+-mdr~;Pe3;LAA=l$an3(-Ibp<$v;#(4kicX|d&btMWwC3-|wcU5s%=}q!cja97&gY+^gN5~u+ z(m|yv2>2LlN^4BZL^5Xo&)R-X;dDh9555R~(#sKYX#Pc8jxO~iZu z*4Bn3=C>J{Sl{1g@ipX7CUIm4F5^8AF`}WW!`G? z2hG1LVHn7u)deNef2Mvk?p-Ju5FL)IDpcn6gu?=d+?4JOkhjb)9AhiL%&oFXkiGexh9&V)Y_P|5c;(@_>=oYmXa@@vlg+S2MeI#ZO?Q`hQqHtVUh!2Y!rfF)KNZ`xtPh2uURBHRZXG})e;>o8p~r=y1{ zmv>0EhUx;=rvFAcS{pgSK+)Mu0n8bpPEi@rx9c2jh(;1{wQD!F(eu% z?_CO>3USrW+S0^$=yQ0?O|ossF7Nv`^4`a)zF8=AZL*SK*0*8@uopBQuk;+9bdDm` zA!{Dh_amOOs#NJbgPM+>->=BZJ+8oSPyGFR-ZV~quHQpmasH_?i9mX5n^oLdQ8FZ3 zR-Z+jXyP#C%RXV1HNH`yP!EmpOy+>YcCQ&`VejeBzGY#!yQ_EK5=`xajghF;hw^_< zYU&gOI0CLqXwD<;#!%X-&LcqrhvU;l5mD{@5YT#OV#QGW9yc-uMN^~Op3Kj7Td2_U zEfg-WU;2K}8LR0TiPIM$Vb!{(i^}~18K|QDv7_z>lth7DQVy@1H{vW+8oZ39W>Y{s zLNMM9o5XBjtfy;~v!ds7_z=d!%N}v{A|`P(R)Wm70dDW(f1MCy&euFmWkjji3A~fJ zr0O9J^0Vt^2ug&_Ii&^Oe}CSxSV6`q#we)TypigHTqiB13PnXXMVf+DJk>0Y9JM}uAi|M@|ld zea&u#V)BO!myyY`r11??E2hZwSJr!j8zYFl38sgGJw(eeo>LAhN^RxCa z)qHjhOGC>Ct68EczwktR_L~H>A73X@Gn8h|eoY(<8mZ0PAY;ZXm=gYIC}PX|4sF_M zOfi4|o8G#Qy7oeI;Mla%w8rv+aeF_MqwyAIpPVW(A%bfV1pp!Bq{vP~R{R^Xb`pSY z?KE2aZpxs(jra2wit&W#VJi?zTsp7=BZtb1PcuX+THLa&=4O5x8&p5yMjkF@Y z{Ls#Sgufjkh-?L>W|lz4VH7WxsHbc~{P&crj&v>7^0MiwitST8*tB4G$;J6;6iO>_ z^Yh@f&oiLj%_>CHrAw*6eW3e(i&R&z{mZGDx19bfdK1EU{ca+Jg#*k=ooD4eW9;_Y)lP5RV5Z!71Lq#mSs_VD)g&mUUBX< z3aSUjHxA8^KF%q)+$HZc&HMQLj3tw~w)=y&t~UL=P2N&H-7VXbPE+igUK}&A!HDpH z=ku$j<9*R-df=MZZoSB13Y75(4~cTbES6#H!-X>!@~F?59|5M5AUWv?amjF~0vb}F z&7JL*#+zmxQ0$01pqgw_Hmm-)Lhs?<`Ih~r1=f$`w{;xBev`B)kM5atS0IBr^$x-; z|EHJd`>{43Bu_7g;iDw&$5Pc2fv1`_yx%tKyB!1nLy*4dx1Wi``V7OTx!im*`3Rm( z4{a7wKdbYOR7Aq*EuKGA0;k6ms6M9KZDf=Kp|Utzls;+_vs_@G4f?cE(ui9nWsn}m;0IH$klO@asC9fM)= zzu3q+XeLx0SZvc;XBAW!4ec{ZHdrZiImY2rF{$Z#2!Qgbtg%tdU(u~WrX$9-tu{uh zIJS*sj}8zOvLLb}^kL!)lus1l#yrL@O4<$N0LROMWJrMzE}MIpaS{6Dpqx_zZyPm! z`#`cEbu1+R>-@qlvMD*!NZaicDHk9Uz_bW^-U>rVDcgakQ9rjnj$?oqt3De+ha>H8 zJEGr;V@tMH3tq{4793;N`w^kM4IW%Qepe*5$C^jJnIaiF2z$p}F5Nd$V5Ve}c-$AiFq6ijR{h(k+JKn> z^41h!52o1M{4~G3wt>z#dj0$5VxqUpF)i!Y*J2wp#LFur@ zF8D?9ExcM!=$1z)^;Ia%SA0Yeke%L2Houd7NMBR8neH3~qMmO^bQD$U^1C{_ni>BZ ze_8Gis46B{>yf38hEDn&fT%fDNCmeWzUF+^;>{l^s+R#g6vZM%_S0w_ujRwBcn z3Z=D!54|gRzy3Q#NgaJG=^MRhc2kT@q~Ra?ycpPi>W{9C>~7Tk?t*>;jzm8RYE!5Q;$RU_|FZRB_Pl|8_b@>NB{oH|b`l@!UBblv(F38K93lUzH;@`$0!IkL52WS+i$1Zxv=LJ({ zm)>R7lr)Ry{^5*VO=vVbXY|`3^H>EWuB;APL$j)DMn@4sjgpMf%$TXCT{=~jHwBO; z_~255zi(>wJW4(0L1us@DD+WkqvT||NGXsHcCYzNzL!5tez)9YW65;3kRoL!S-=g^ zx6>?ddIhdDy`DV@e^|1hKS~D<#XP3>ZXq+^)DYX}dQ)#)x5-OR^1O#Np!tU=>W_x4 zR)#xn@;d+sMl`PZ5;c!%ojs};w`FGpnpc$HTO+MDTX7f?A|JC~_s@pU4-doAp=<^j zS@Cd}#|Ej)xbTxAIWyu!HqVh+3>6=tgk6oHc*;gW=H0qG#XGsbHGq!te8+Y0g9bs* zoGQbH-{GvPag#TR`@S8V59sC-G~@`S$z`7Ny@R3hgT2QFTAj6fAUMoIFXJ1G0{`eP zy$?xJ9AM(K04;Is&vAc!I5QpH>w$02y;wlw!!Ozdea?;5o}~ ztBQif#qj;Np-<_UcaWmeeJVa4ISI!oR>8!2Mi|#<(k$jeTq-`7im!GRUHv>+~SSsV>CuobzvRJ5) zu!}e`D92x50yd}zLgUrg1PkdgRbMyf?ax9Db=dsmWA1v#wem=q?M}Gkr4JlBEDuUq z|M5DyFdS8&qDl9JW(jNE8MBS;Qb`(II-p8e9Ial&T4$H@F54lg!Pv>Si@EjN2L7aK zO$c>8?WQwRTc=YpZQB}2?k43Ww43&h9aa>UpaT=)@koJa!sjbZP^2I~xm!nu+X(R} zML61lE*ub$H6aJsL3|-5l5<(|a;tR0rgd+w)`l@SX~zF|5txg>&3vR`R#b-PP-_$1 zY#N4R;$0f%3xw|4P`Ds#ULkj@I9XXHkjZ6NK^zFFGQnt{}H3{nsZ@h@4 zA<-infDs4otE3Z1ppn*jh5`@sI9%ep1wHh6yEs|>@uRe@V<9D76M;_t*buX306~KC6F)7Ch>Y>TBg_p_o;d~tBqI$ldFkw9ohCpaumABl02MjXX0RV zep9BP%3}4WX77}h_G)yhpe1n#v#bEHJH!r*S5d@HlnzgDh-Yf!nm%WFaM{6% zTnF@yI3sZbupPdCU{~5UCK9({-d)SKrsgNf{;Beqap}hRu_D{jk~7Dr99)=9KdPar ziC{2%g15wwy7%O-BVnu^My=&$PBXYD=(E7`LWd!j#Obd{p1JpstYC^KaC5q}9G?wW zW?St#IaKKziPD!D&*N9Tf$uA()H(u_nkYWub{X8A+H)pZo|#ao2Cp0u=K|NUYA%38 zO?q?WRBV5UxLzZEBpnb;Z{GnlMpX~Ip@1Q~_pw`Q4m5Uy8X7IUav`uoZ| zW8?T9IV&Azgmp}v{VuJu6Jjy}0*Ye6PQ13E8o|hfP8D~XbtH!GdP|`JnhIDZ`eQnI zM|oJaJ~FW{HoovS*4%rIctu&(rUEl>v$#GbA6Y*PHB(+v(hCVg&1p!Qeb5qO?j{T$ zokmE_%?twzOMKrXC`i&IX`k#qAsSw&)(=7p3S)3GC#b0W;1ZhcSw@*mkAk4w=8vD#nYj1rfyftl@Ro;|n<+%F z(S{KRV{x!HL@XTB4*KE;>Fh>kQ)|z$W;V=zaL1C;}6L^KbRM5i0rsbrR4tmNig^0K252pOV!+%SyVZXYs>Y6x#@@GTrk4U2r z`3x}vVQ5a@UmT%pD*Z>NO#C>%h>0y6II(Yp8o?-ibDMj2`eH@Yc#s7^vp-#zac0Ym zT!RP{5zm4_)E(l-mC6uS^cA-T&4-vTZ1WaBOyu!>f)Ns9b0bH0e^}*L+wQhR*2~zx z2|1*Wf=qg4m6`j9aNm=xL>^zKjSsWLoAEG$EK#YE>J+Ygg&<4>aN@YyO5Va#e4EQD z*o{?MUEmuyBj%W);zt-Ul@VgAq`OyDqTftUKcmYjc=b^5(h5U$2LDkOB%O`Qu&sii zf4+^dV{WMGG;T;WYTKWhfYNw!LmwH{h5U#2_Y?=$Wm*?X z$#3yf>{RsvJH-yxe$4(mLjX5m90wB+xAlUS`3iU+QIc969vZX)yo8PwJ8UD767*fMN=^M2zqHGye6rm zdyzF}Uwu0F&Y6dFL&vc2@p}MT)y~Cns=AQ=PTBFz zz{tt%ivHTb_IS13kU^88PjmUl7=@#o~qDh^~~107r?W+Fi(@~ z7W|@c9XrI^V?Nt*1UNUu%xfM`#~YSwh9$MmV~{N@SX#OqBWCHKl3ZysFuuAIL%ptC z32HOqci`RuI;`xVj$VH_!O*cRJ%#7K(l-KKWi4j1P7OmquOmZnhj^LE8C}S6>2+^* zC9MwcRqp`+>!Xfy%pSre{jto=1^w|1`cA+NzB8YSKRVi!tEzG$XRI&EmNsH~e#3FA z05tqiVP=)K5Kp0U%)Hj!{3yX(j#5p@(T(W=OUO>r z+3hpl0Px2%@CP&S#rgR9?5SeAHC0)cU^tDg_6_}YB8(gAz$UP0`Y8ISojxD&R6fe z@$TyJgD0z#(-V3bLU$p0E2S?K_9&)|@ZmNCE2D6mCAWUIvw1EoqZeogC(JpCKKEo< zKEDGf!M(uZjah;<7xT=uUd^S5sX8YkHPGV~JQ<^D5o3>1$;&NaouA%5U47`gKe&4P zE8ki@`W9^htQ}s$002M$Nkl&-n^qaO1=@|g|t)I z?NIFAm-&wY(R;epYm>E>se#%JJ;l`ecJFrK7}eF~R4_Lcpi)Z$l14ca=w(sR8S3l@ zzT?%^qX$n{o68OTwW786QEvwFjpg10kVo~|YzQytiJ+lqhcS=OK9&0&Kod;a+Jq6+ zCKvp4lrt=ZIetQzqzYvQae_@;HD?oR0CkvZ?QDXV6`=jaU++D;d%Ajj{|Wt}68xz$ zTk|$jVI(@H{km^N5?yZyureZ~-Y?H{j-pQVyO8^T5N)=4>Z)qDn~-A3==kVRBTVsO9PgPHouj_e-zw$z0OV&AcsY{M-U;v#(r!~w zG=byFM10)L+6>WJ5Fs_87XWS)21uSZnxkQjuP|17+ zWhxufRw?ZVBVx)ajw)Ox7>xE%qDqbQFTYIh3U26U5_kikZ`k19J{W!?5J!_nrBV+! z7?HTJ>h_fL7B@>xBu?HgM@6tKyb9VS-%@N!k(Az_1}m(KIzXUBxI){Ja#1Z{_Y89p zO~yKa03B0Xnp$dX6FiK3c^z2z{Fz|Y1sjWIj2%~b;@9(7uwxG;R5?4ezDbyGKE#lN z_R0hU-Ua5AIw>hQy##<~$@t^EAL3X}Gwy)ZQ4q^mM=0ls05wJv@;cUvpcWW=6d?fK zL!$JPo_Qmnj#UE!KaR>Q#`GKzaWeqMND}_Klu*ZYrMOPsRDM#1S$P9kGRC_u`X!uA zReJ>q3x{%_#~mMgN+op({P{;T29kl<$>5I|cf8MpI0j+(2BQ!W6j`Z*p!cUajt!4M zV;GK$os;q&KnKboLw5Wy&A4;aLhux9vWruJsI?Q^F{PU8->Od+s1V$yhBH1A?R!WG zgRr1iah&EOD?}Q6O%@3kyI@1Wj`n?k3XVWv%vQ0GHY z1`vM8XMi4Z-tu%({@JfdGp!e{^Oom3p9-3KH_M5bjUGZQhl zK`cvh1=LJ^J2JwuSCpiXZ#o7JOtpVE&3^Y0{<@yCEPgAxriB0COB4;~qJAI_^q@1E~)5xue4 znccfj-v@x;DN^^j&`&qxEzc46BSIjQjNQQe3*jwP^7NhNv0u3V-1G-Wl3;(72ap#szC7D!-Lco^_N*b ze{!*U^K0K+J$UQU>g?7TeJuekefokD`;}ny4P#IZO{dEN!6KsQ&_S32;l`zxBX9P1E8+} zSe>1naV^JdsRG#DH-VT}vvCtfEr=1ltGo+M?DwvDfZrw8KK$L7RCH3ha5LtY09ECc zYB8x@y5N^^CE^{FCETMSNwK6j&V_tJi@8Y5k%&+{`~|Z^cN!=*fOqacTAe?+T%DZY zmYWtG?z`zo8G;hT#OHPw#9gT#!?`#Qeftkx?T;z{`jmeA58p^cumABTAlIVxcpwQF zk`ZsjwQ`7JF$``H+qlD6o77SSOe)U2U(jbk=LX zN-muiK6z#1p%mB%h(K=rbpxpQO+^`3?R{c!GVNeYWb%8h^**CxNx5=Q!ygBo=HlV5 z#@r$XW^;Qx#o?eCVK!FDq1se)PJ$O(VMc%nT7zT#rxt?3G^ zuet5&9O=9c7sgYDApr-TN>yqse)zOSewEpQxZaMU(HMh1uLAaJDn?hU5wFy}Qay_x zuRkX18YAG!^8AkiBSmKr#WXIE{hhe!igyl}rmIU2^jvvWesD8y<(6R}(2`gtAS~Yz z%vL!Mz||I?xUG-zrJ!{C&2I%@A+|@az8(&dy>;7Q6$HC1e14XJP%y@>i9-TC=KO}c zdRtYIR8%oICR~GvW#*K>4w-b;;A_#-n}En5KxB)q7+WtO`0UZ^;r=$Pczn`^jeul&*d0Ku&G_Bs!gzEU5^xQN8K||KFku^L zk2q@C3r{a1qpnk%$Ubz9`;#jj5@gs5rqKo zlk}zI=$J|n(o}$Af}_+?f;70%%mq7iJp9L%sZXrK$*WQ#925Fna17?B z9uAHkcFLZ8mJ}@QG2(8KHasAmDgcfMtT1L_>zi)2n44TjVr3=7miwRC=wgIM`mP#ny3jOv=5qu?oSI zkE?TMz8h_~5hyPKLY$u_7sOeq9^rNu+~vZA%Q1%%M!9Sn#ypG-0eNBPMI7VvU4eK& zV-L?k2E)P^IUKV@08fQ8F6Is7LG+A*p=S%KxJ=ElX6?7aNxlwep9esA^3lg;%#C(E zBal7?@KfW0B&}4hgzX(?$K)|R!4^o*x2w(Pq6>wgS62;-wTw1{7~^;{COd1eL~E&K zFG)Lqrpp=}K@6!V3I%I$A4Bktqhn&;ksZ#y&nkzv9DQu@-$>Up0=xmFQxTLPdUm(D zAg)Svd=d1$_jzCm8A@Xk_qb3<>IDr?gxS=zi1FeUM8XqM{X6mCrXQO>$l-OW;JUIy z=XzXd7ofPTYGDX)uaj*(ha$O5DeSawJW_|0*E5hyPK%6UkO!g8=X zU|=t*olHXqUjY?oM_LHe$Tpu*ww}W53))yJa6y*o$xw2zkMJ?1!2@RxD~Q^N3+A0g z=Yc?lXyZJtyD-48A3cH4Vs^1H!6?9;Q-Y1?xR$Bt8{gGNV2?Ked!M395poJib|Dux zFlSJuA;zW%T(w@oJEKdA6mG`-s$e>9e&+VfJK$kqpU3CYTKm!R4I#N*XvhuM3&Z z%`QkCb*=mnBCl`&2S?yAM8_nqp`*=jT)DLhJD;j#;ElHL2vj?IdJ_JrS0;Xw!2aIZSh#)kOxkF|H(JJ4hio zsQ9lc8Lcd0NfG-1f7E;V7{gCeXfLK3gx>EI9H@& zku!94ZxMW5P8cb-HqOs_Fb2^vML_ZWD{n8ZX9E|;xgJ}~F^VyN+sf8!8*uBtu{0wv z8vF(*26_pgvleGZk06>!CSGs^(J>I*?m}`Ge>OK7!mIVP2s23AaHS1_62)AR!|h~b zJ7e8SHbJum7{h6`@Cu?Se@8>f83T>mywFVsj`d&+BGY6)BKwfz*wNxdDVvLaEk$M&Q&o6V-Oj{dN2mD<41ph$VbfqZ_Q&7 zBl}d0#g1C$v=du{4n5d4|BIlH`hLaQhMxjpz~ek%y-HSN%-zE#p95s^n9o|c`yNA- z7T2p98|Qjdl#^fC2Fkg&un*|t-0xcGm7>;3vv6w5i!Y$b#<56;xEC4n}AJVcO zj6sBZ*6$cbm#3P?JU9n&{LGGa6caH!$)iM^4B1|SW2z6=Sbp;=g+mQ({C<-dJaOW;y!INhR1$v%AfBrj>zrg{I~mMoIMaEdyCQi6 z17qbVW$Yz^m#_Btv_dcYmce~wsB4TqBxXHSko{n6H-U_i;F`xM4}BLf%47Ks(#FYf z5W*Lmgf{>Ttk0G#a`o&F7}wb=Q_pQKurtz%q4QjCx)ntyqBYeq^?7XkVNsU+bltX< zL@6xBO=Msj8O)Y2>NgvzjB;D`a?6H_M4i~U_(7qY4b@9u4pe7BPWyNe-gwkL)&n5> zp|wXH14PfpkwZtz3rqackv`(addLxwmjK~B2a)rzuEyfC>913-I7-Z*%E(tk;493< zThX(l#?`T9Q7aDx#F)rg>~uvn2dyBpyU9u-1K)+4PAxEuhXB9Ys5#IfS7W|&p)cm& zVy8k9{IY?H@x>5awh2Ivhu6tM9v9%$tRZC_5(g)UmO4&+nJeR=HQ#C&czc@x&QIK= zTgDs9E+eqRn}GRL@}%OYlCDzO(ZiJpnZr|h^hJ4?OF>pGa4eG$W8x%Ac1wcWYseIA z3(IhckNpw^zhPK#Mf_KJzPPwtU7piR8M@?O9&hDqxsT|Na~i4FQIfJx0gh9A^ubj7 z`Nl$x*m@w=m^g+?+iifWV;M-sOY*>n#cp49enexBf3oF?uS>F!8kUxntKo$>C-dTn{C% zt>dUo(Cwthg;?VP^-zrQ07Z^^?wBv;=NNNPQC?Tb$Tq?-%om+(1crwbFAL(qcs#KX zJ-f;Hi#(tDzK`w}^s=F8jLx54tloL^(dyy-C-etU*3<*}@>9M8sZZw1*1s3DUf%$G zvQnW_SN+~xjWXd>Ijt&`PC{Us*kV@MN{Q*wEi1)b|2tFyotoIG6DpE@Qqy z)gy4sGo2^wc=2N*<6#Hu!5Bn?@M7Zy$AMTR;I~K1XMu=`538@&MjYo-5>CmoPGC10F4_K`mI;aRu`9-guvE{!Mr)HV+80WKwkJr z!o#g&9)~{K9T%#~e!DUTKqdAUt$-}=)Z(czJ-+%X5ntX|g&s!+%a^!GVZHJ*R)O-e z0JZ1wG@h#KH|k&nFkTo}`U`0Ea}M-zr@RL6P2lD(U@&ldn#)T7BH+myW^53XFWQl} z#M$yuTUAuS6$fk{*B7@2#SKB??1cnr@}CQ;Yhqpf#v$=qKYSL% z`E?_J-GExdO*20N{sT`mcrubybf6=y zCl^KmM+}&35kfYf#h7lb<>S_jfkRBT5r9x{Iw_#$bdJi$I3f!4$Uu*qW8V>o^KM^` zo4Dx_D0culbK?YNxnl>V_{s#2aqO=R7E`sxP@n8hIB5N-@o>Z#Y!J}FZ}zCOT^+WW z7!FZfmu>{+w;bXlUJ_W=9BxlJ4T0#JW0w(-y8w#7$sPfLRB?0MjKGUE0_7!u)@6$Ts!wFd zgR4Gc0x*coXnfHgWd><3zVRk!&u=(udzd5o?HAbN0yVtZxYgixp$pn-Zv7NZvG^5S z@Zhj~Vi%j)xdlM;74MC7$PvK#vU7Dv-uDBS-vRI()rlJCtJr0_xArnb(Fdz7B11HO zT0(I#a*TnswHRA$(QQ81%x{~N`Iui9tJ%;NyfE72F--E4;=0AsW;omggp!{*7lMr- z_-@4M5#V9F8N2C0I)Ilq0G{|d`)v_`TkJA>Tf1bBc8;kGQN?u=Y_>U=4=vk;vUxUK z<4{BN8;`ZPH#fg5^zvgRz~HcYx*GunBR={#PI+Oz+t|n;cL_4hVjS1Qen@geKj-e+%LW5H?-VQu_+)*&dgsB#>giMZ z#a9eL@G0F*6GuSD=;1q0R%;qN-+3dZBj1c`XHXWH;tRS1_`TnIC%Zg25Z;J90&jioTdSv!FK7e6uLWZk(LWl~_)~y4Lv@%D zC@%q&Z?yPrw#RwPvKpdY7Je=;`XS@a*=%)-lWw-m($|2;ff$b^#W-v|n$BQ*BBM<# zdvU;g8ehj+-O(<1+U)fG22k_M9e_4RtFyC{)t|iagf^YGRv-WPYpc6=Y1lc)inwVp z0vB|b^wt~qSKoT$0gc%$S@iU)&~|=qM(OY);GY7>;-Fcn6W>y?U7Wt4;H$6dg2xr# zBBiZV_eXn_5f$|)+nT9uQ8yV_$lR)tWqLx3!>}gEM6+!m#8xrPL&3vMfE+vy)E*zx zH~7%cw4c$hy1x15)74vVy|H@b?wYP{Hil9KMQB2)4bNO>$nBoW#`Sb;hKD~L6&60m zG(fe7;0Uq)hmj0OijCmp5yjlLz;V^b&;FmDo$4-Z4;=s!cOycA1dbOeU)-8HNkbcAM-FGkf7hR+h$tC50 zMaRKfty3HEH#W%^V_j~rMvULH#}RVIBlW#wQ-!=(hxp_X+2&9()G8KfHWi^<+XTxdUcj1H2ZmE&g z@|c_^OYryXuvvY^!#H{}9j(P$&Ad>NkdEk+2^Tg2@*~C;gK_n2vYBF0Vr*zKCuGBGTN^ZFu)KB`V7k{thH0B}D9EtfngTL9TqQwd^h9NKI)*)l6Q%LFA@FD% z8XGS&Ih>th#n+qJBFA?gVl&{4lVr17{7rk<5jc^Bz*Ene`BcjJOpC`9brF8N_-%n1 zsl}kJ7d_j^E|@Y!EXFvj?b*d93js1%*rs5ch^qQR-~|j~3Wz=%7sT0%0_G%8iV3Al;lp4rjlNCP3QOgUJ9p!F7Y# z34r{FKs>=cJMzdqfK@K_$wtNr=t*p)CfHk>#*l-38f-X^F}s4KDdS|pk%MD{A&6dw z(B`^5XAB@?QgzVL;QYz>knHw^mNXO3@jOhxZd^J7?Mnb%8QREHO*WmX$tG+Vd^>_T z*c5EaQv`t%-blp7T0ISbHyg(W4%p?r06_?rQQjZ+(3AK$h1ZF>CuqqblL`@gYyw2a zkvcO~lZ5mPpGEi68_Ni<_MKpWw9oAp+6y4|vm?Lg>SB}sTJ zhea2^SZY74JO%-18vq!4cwxGEzUl~+PXWpt@X606wM#=&a}iFZO$@ z6{2RzgQnPsFzDcCpWC1|`>1iiJv1J)0m=7)9E3hMoY>JZzSNnKyEEzn8 z1eO^mVBZMHVP?Za*9$h&!d`4FF<-GUtk@JUR(iHgzzqP%GKUE~e2`!=x3vyXZPNg0 zz-b+7xip+pxt!uPrm|T;T%ZbJ7t8*Xw)x60OflBDZ`$WG0{lskUDa&?aI16EKoY1x%zD26dvOuVp!t2`@2e1kZkf%C&~WWY4FsmfddXkzR~w#gLEHqRpc zP}MN85Vp~i`K)z=)(72u)`He#ZcT~#CTx{Uqe>Gw8*F2cxPiHlR?@EcDtuOr=WQ1J zJ_j7zV5O6yirEZ#Uw6*+-P!+S;q&6L)(PkS4Y=%M057^Au z$;m#&a6Dv-?X;k9oB=yHc6IzQo@Kg9+hKJgj1L#2CfEcMuIvFLK>@1<8z1xgAb}4? zdg{<*_!5jqw8EIb^xukzSeBz9P!G%nQ2{EIaXnihxRI`71mxF&iJ2#JIs-JZRX203 zqo8b~2}Iy%t7EMbu=7KO7$a{*VKT~<8H=X(Fq|za<6|x;dT5H253T?SHJ3$@YbyPU zu_KIgyHJWTQ{5_}YIIC1YgCzso{Ue%MG(anBZ@rdFUXfmvh(tCRsHO%ZC?U#4dcZh zOSD0j1(+wQPb?A)hgjJ85$s}%3%=ku#)B+aS~D|7H;J+-XkLKyFp=X_VcGm{!-gY= z)n4oB)f3W| zf;5{_c>P(Iao`AZcp?)@YmiY}kdTO*fC+0#20ErPH|iif;}jIJQO@9~g|Ud{CGsG_ zz`TU<1~V}N@+km~ZW&;iY=|-2vIQVn>X?+80qz@^-EZS; zh916WGyF|^_z|dH0*r6{?ZlMDfnv&L0jV=>A5FtF^@`nn{VRH1VObs6l zv0%5!rsDt-0KwsSmD#4wxCy{MeaPZ5-f3*k53I#mo8$AsWDGs^u@Y&R$kVg2Ux}vp zBF~Nd-WY-O5@6INpA;?tDU07s-l~9xMS2obxh03e7QW>Kh952n#x@R^?J!*05Xcxp zi)=;;hUgoRC~9Me*QBnX>%y*LtPdGq7HZ#bbx_XUk1$FpT2NGg53!Xis#Z_5?ISPY zX)wVML{4Ap`_yz3bJ!6GcL3w7zooPjXjjFe3!p_CcWP;2O*zqu3;Y%9C z;Nbj506jiPF@D~>@DfA;X#7ycY>Xl2Qr&nAZ~CsS#n)|*Ct*xsVAza0feD)YCC)k? zu~AN8NeLXu{_GSxch4^4%aU|27GoBE3n4*7MNE?VB<9#;+aDQS@2kR zgNFrBYg`D$R|0pb@|w0R7jAt)w=g+Z=?e&Q61Me}7@Io1Pp-Ngy*1?D)+& zUGP>{TZdT+mS2`|yz&`uf8|}py0s6u^`aZS?^p%X5=;=I~QYL=2NmM-m9$1O%FMdv<7{+Ui zB)7#Z#L>^Zh^rrn*9W5Hy$Rw`oY?TJb-`{jS}17wS>lRb`%RZ|;E?!~xT2#YE7m>r zU4T1!LMK1*)s!W&gD=jBxByZy=7@5G49*}jtk#>@8^g000b2OStM&2v0j>X|NB9y( zn?klIdR3UuPGYS~%*5iaND*&WdvGRRHJH8I8irc)TMc~oV1DI>YDrjzY65(8nC@m5 z#LpPJW9s+}B|JJ7JzfL$+M2hco5|W9joBEp0GJ_RgFng4AiD-l||aU?v-{>&nq*FL<&o4GO-Qn%cMMO~9i!@U_)+@K}w`sSRY;uSWAFEE(O-StOAyHum93 zaS_XA*!6+0)yBhg5)-%S>`y-0zz~Q0b_AX6?hIFL#ae$XR9eBA&%+}~ja^^FLEh^sdcq{AaC#`=#~}qj#yC?W>=Vzc1hQmWMWKHmRe{s!qNNkbD$iZnP|`!F0xxqGoSBm&KVub;cWs`b@PNVCKz- z9E{gkD6XsEn_kDK^r?wC%we3S_gafvg7qSeDAs1SiFPW-l&_{7VMq2lzNhzMd$wNe zjcxZ4z)Jx9F_w$X>i3UN*6T0gPqCA&x&7{*%9Ryv^F?fLyv$$6 zSp3p9@{DBTVt-Ob5z!v{*NN?zN^o!nvG{MK=Q#pLm$c|_ot}L0n1nApeR6(Dw*~%V z!8U2_O}bH~g40Q&XW34+wH!-|Z4yNnR>rYE=p@>o??#-(0$Uzxr%eHMz>dTG5TY&X zc@uD;#v2b>9L{5cn7hw_*!bDj!)v!1$-yUUz!7DUN-7&mzY@b-iC= zu-2bEeSCR&aq)#?+6I1iv)R6Ra&k(O6Gt8f)7h-P3c>-&7I7ro0$?-il7qQbhfn-i zoO1PLUCS|lw815IjUmPS+*UgBC4b;HE;bn`85LYL);A-z1q}+LF`I=(pJB|G+DT{4 znj$6lWW3^gn4T@u7i|_hU1uS>k)Fi}V1=Kap3)zu-M)EreEGY_Kk}K?n{<)=-P1EZ zQGN~To!4Yjg_|f-FCxxl!=beYI&6Lj@>77t#yGZzAkurE1TdE0=#$OExe*T~9HLWy zgU>cPzzV)_d3cBkz$h{_PW#C1JeEh3m7Eb#$#bAsTNN7BIU2(*#TovleJMs@dq#hL zo!$cY-B-Ws)i?3ySGViq)jy|A0Y?1gNQOU0N{RBQG`9zE5Z#@H zI&1dMY4iMZ3^)E4WCS*6bg`#Dz5dVn(*P-I{hPcX>3%ky=%}b1P1d1ki?aD+%YFr+ zk*i}_uvh`MIVg<7wLbU382uc_NVV(Bh#JQ+$iY}z6fO&)*p)~z(H3PxEwTAa1xcgK z#vpCvG1=OTEZUsEAg#~QQsiBDO4L|7G~YPWjc3OZINH)>{^)4^n~38#{?15w_uY#p z$M}&zU6ODPPog@w>Bxwcg{4;=!YEAbj8Irv{IVH{4|smi6K?r&n2otfF?_NMRPu#Q zfMcRh<=AK%HbpsNOm;sD(od_$FlY^D*I(AvSo=6G)*OfoTektSOf-UJ=<7o*sbdM| z`YL=cGSlb(>-9Si9z9vDH?jfHdjdy4^h2xPqt*1=w{GE21s>UYO9K$kg~&Fg6O5G> zE11Tv?>NA2i^nk}z-GF&V1jIVgCU3e0M>(T=73FQzA|_097c9`RkG4fHXb{PDmm&A zT^c__U-~cyTOSS8I7KaP%AW z-T)nWxaLlF^k=zD=WOqfA~1%F6_}mhAjH*CA6%)$5XfSf1G=TDuBdLeSjCO(MibjP ztoh3bl={qOn8<6gVOS^N6xL#H+M5xW8v$DI+uOJ3*ME*yzkxMR`1SD{pOg7@e6syV zj~-oaH=7l`44@;P*ZZ_Ts z)SkG^#%)+i76`k`*kmNYO`=}u**3FCV*FMS>od6x8+k+?d*HWRZQQga$Aui(!6Y41 zA)j;>7^EGbJ7IH{_pe45|IMAdcaF}_FMj8>kKOraG&kuc;An$40k@z3{A&FpKXPEply4H}(cKgm(9=pzO$==?xTctjuW5c~jGkGKGXxzu66x{cu47xXTy zV+_@W=&{(LDBwwFfw{CR-UDxh_t*$*?%Y11d;a5JrGC@c+9iRK0eEv`O z0sr9Pql*jL0pw+XjzBwyrl+*yNpM-Yq>=dOON*5jwE5VsJg`N($wnRZMvevx2z)R_ zh|4YrAgYKdM;?30ME$=$p3Q$gG9W$y$;TUTadUJOWRShOZXa5>r$K!^FP6|30U z794mfU`H^U$ya?a|4}$uEKA_19nbbsy&G_6UpYOJ7=@ z{mK3F-#R@z{nV2u=X7VVTH9nr%eUnEuDHlza|{XCJ6LB~7pTeX&^H;>hNA3;59~_L z+mpq3ULbWdp~|fnv|;fEBcE)TTyW?lS7bQoFrvkSWxOk*x;ohBvhIj87=|mRmXQo~ zfLCP62Q}&R2@PY!oN8sLI;D}il>&`(sTlN}o3R297_r9j(KG0EJFgNLwz0c`PrCQX z&DKfd(Ka@b`~Pq6>S851u5wRL_no=_yEeNkXCdK7wjhyWa4hVsKxBn16bVHEXY&{# z86J2dyhg-7;Fmlle&PY7_}MllK?I2)vH}U)L6#MRWt=!5V;&MYik$53{h6Ph=6t8> zRGq4>?wPrFe6yRqeKlR@=R2oPRoAJm>F((p$ThP7-R5Tj(_0-<4F?1I_22l-J4Txy z&hE~}boNJ^_@GD*5zqGp^Ly^;jp#Yyrv?M80?dKC)qCJH{?i_~U3*|g_w|SCWshC~ z_Ee8@oB9Dc?-E;00Nh2m7SJm%^q;k7jBB-O0cpB?ysywEdr=6A~M)0Bok4?8L#v6p$i(MCn{VAg) zRtq()>=q%Vr;g!bk!nscw^T^Yb6R-Opq6>AS8_YyF+FBYuF@Y$cPG*?#nIEP7Xc_3#rrb3q3yp8!v=Vd# z&};~`scZo$oV_#}nkBfDZ34DB(Fs&|lq!y5v${^X%#TeMQKSQamKr{yi%12f9i-QDf}-i^Jl z-EndAt916q5B`$*EBV!E?3F8Y$B*wHR-YY@s~dC`k!}6Lo7F6}=V{-L3&p z>WYl!vZ0--BUG@Tgvk5vRC*YwC7?@T!&-j&Ipk>BFMFfI5&hr4;x1YD?25CN{ISC?si|J0y5wA~1;``Vbo_!={FV ztPK;}7~bALPb*p4<*!TE;j;axbr~(GJ8EpHYF6nh<^NQ+P!G`Ae?6a8)uU(Lzy4ja za8LcEtXn4lfH4W+tH4L^E5CH(#^f7Y+iL~QD0UK1$!9(50ieI{hH@W<$Wh7%TA2ix z#*Q{lcsm(1CIm9pzQ72NuRV^2ANFQ;i_i-0$Tm9xh+a>-(k77!Mr?E`7?u&R3#;u; z>D#*p=$?PIyR%*V==%OQ&iw4gmnc`U^miBVQ@ztU0gyqBuL2mn{k(c)Jf8k=ZLLpS z_pwU8gqm5rn28GyKSAi~O9F_^w#}cQ;3Qmp!^Hvz^*e+VJ zh%dp$rRZR z;y387z-Tmma%Z z7OnSo#L*qP>De=z#s0y;lXrZ8uKH81_9$l;>5CAwsWmDg(#d}okdwjJJCsEf|upL z)-`FG07$6ihz&f;Scm9{XPQYj0WgMjej`p;%`u!XF-v23l@23MjOWpHNwQZplfH9=3qUVV5l;9vMKeN9-{pPm0C^$7EO9q-R zVJ8QROWL874+zk*nJfEV#yDW;gIf^J(8?eevGGA6b|t^92sS+BSbK1KkQlwecpALf zoXim>4$;S@_%^BJwJ;YKQhIHIng=5NJP5olf>{Qai1bu+0zE*>eslNY z`e$m&O{#X`aXKWI)-avWpFjnSyoTXuBd_c{%3Er9}6P|6Hu|+zeScavv%~Tlc3@?eVU1>VB zQd}oUyehmR*|gK%f>W53O!*GTx!CSF17D}i!aaa1{#%=y#b`AC_QAONB<1FQ_Bf4u zxka|1r==zU_2PruYVqBs?z&vXhFK2Qi zF+NN}W7+CIUTg{Q7_VEk9u=Rc1uBYVhyycf_!;z1Ly~kKx zefjX0heh#^y}bYay}b!t6X+E(jOAJYDu&GPDji7Zuofgd*kv}?5F#8+4wzOlu07xl zNNoIMgjI!!ps6FjEW|`)F+>Or0yKYE7~-r6`(U-hn$|m+CDJ@$N#S&G+QFlz8||?+ zsxbbnQ+1@AZ5vk0nU=tK0*AzJta5rIk60E;yxy`M8)G!z=0{_06&lO_O*-?>>gn4D z)6wsoySVd)Uc7TJVkS9o!w5P32&s6dHCqX;`k5 zjHMkgOh>k`kGBA485Gz=rvq3d`Hi)ohMYek41o6{iU*jhTVZ2TW5iF43HTU9yojU> z@DbC6>C#1RT%C)`V_b?))t{23Y;PwSo%yGD&}UXvZ%htmA3JXHqwkL0&ocY`%ggm$ zYsKujwLyOA-5;5i03RI6B}pe&GNH~dRv~)WIoc8IE!^4YQ;m?M^g43}6rM}krTN6! zQe&-=v_RLTX#^`@$zamdndZ0{>{kHhY3JnZNb}Sw9eN8_7`p;dHcgC*#a8Uv=fFM& zCvsS0FMDY#uRKI(OE$4~5Gm3jV~i+LSkbkWK9)Ab%`eAo_dCyS7l-5N3-$i!Q(Hg3 z^*zbG<3xYt6@XMKD}mp-zxTaznm=@KID78wnX;f?4$)%3yjoNank$LG-~!JdO9&6& zLgV1Iek=}nY}!V6Q-;Y88Nk3}<5C#4WED|r0J-F_Ai+9+iqJZUliLcv|8so{Rho?C z*uuG0&jnBji(rl&>CNwe&ia(+_3UTv*rxmZbo+f$JhZChN9$MC%@m~j(ZLJfs{SI+ z^FJHW8^|WpaYfGx^l?hpCv&D*WsjFw4~qLm`&(lSm)grQAslJJnR z-06uoK$0i0PG$Od)%}*0~^GR^AZ;hV4rziXB!T?<%*#1TP{fwRuc4W&vObG0D%qLPhMpgI7K}P^YS}2Pj`?`CLzTcX#?_ z*?(s|nLLc-Co#R9i_+)_SNp>nJh!q504^!8`UwgSj}8CkN6P1C)%a31n|=KZeI@AU z{5}DGJQ3q5_z;7nGKuBSIt~MU;=^@XUxxOHFNqF0Hu%C0CDSM^VG6Ph_*gHzj}IJX8LPbGQ_A%KUgm*!Z`?^s z{;HaMeR5b`+IipbdE^ulA35g0D?4%yzC{8_$qw z^w)ULm}{9MWcap)>CdoStAYv1r7#2nsbxmBX!66>1Q#cKllQSTb*7IbEIFp0kM)Mf zp6e%|!x~|Ow_+-69LmP&T4Y9V+v&i*PA9TKNGy@Czov)M(wL-23 zso_QLONP;olMkf@I(!Q+cAaHBp^H;7QaqEiQzAh%$}WufX*24wZP-MDxwa7?V%b|t z7FF|8!y@tVU4MOjBj4TG!HYb;((lh6K=LDZ=)=Fid9NCLB7KeJ%jL^G`t@Lzz4)#A z_h;Gc(Y3Yw-h+cI8;>RxjcCfL9#2N<`-~oA z#m)T5LY-+*wK$S6h!+u0KmjixjWM#=hNQ}mXFM*JskkgiuC+>{^BB!!kCP?7$Na;0>cQ?o^@nu+{}Xy{;O^0(oe0=7PXzIKv9->i0RhuB z;M2*EN@T!@E6=BGyxxRpct7yddKH^(X|q1*h8za*5i4b{m65%b|r^k*7&n)Bp0&w)&YxUN(S@j3AN%qI(TK|Li|A6!> zLmIaAl=6Z)f+hn*=*Oe4KM{E>V%`e9&L;(&i9Cgs0kr7SAI{8I6%(enZ{vN#r2aUb zkoOadi=FGV{;g1KjynOcAM4Efw0)M4bM+1w#;W5r(VD%3E1~q85;2*=#u&}F`O%n@ zpl#CQ0$#DTxy5VU@p$|?y;1yJ_cN_DvLYWBG zs0Th#jc0#U&FbG?-{|-848h^y1f!@jlL1lGgAz1S2aJ?DFb7gXNn(g(U`XRN-pMS{ zY3DGeqD3#}`8v!5;CbuE6x=|kLraPAJR0J8n6OUCICg7b3x{!d5VVDTqT-6|8S8do z{o0S4d?2(NyOKb(OaLN_Frsb|i@H%tn;MGuSU>=+KvKVnj1I&HXK^~yulI&@w!g7= zh%5a6T$a7R8r)g@9S!F(LP6pKOT6iL2v2BRwog|Y7Bw982NyYB`p^1a`VRzGCe!No z^1T1S^&x#dpl%wCstgxPXav&&fq%wK{neZhI6(3W$LLK-U~VYy!aH(?f85THh;EI|B(K#=^mOYqFv)`q65k6E+>kn^k z6(z0^OeR@2rAdLVn9}SyqnQ)gK^t{G%?`wxtBMf023o@LqKo9r^3D%n8AxI)y!J1X zcZ_N0O~|50ph9yQ@L}@1dIOAUkoa&3s4yuzEi-g;0`NY8SY!f-+GfmxiG%%Exh}Y_ zxIzLXMA|G#>*JIJEFrW>4hsx0V%Zqypu8;0Y;A3jt(BxC`L*kN6MF9DKk}mZr+%;a z`}OnLON8UqF2*`p;(Pv23BM3wlXlY4u)d6(b#52Q zJj;~#5h`qmp>Ph@=4*~wLyZAO<1wS1*l% zjk?|si2ZN6nD4T82X9y2*f9js@R z*|o_T$4XGDKa81+*|JFMmc@7I*}VU%>cjupdEd?tVt-^>WY1D(T)8>U_HlgR{~wDkvz~AgK1#R=3Ii@GD!QWP%elWDuTEmQ0ZZG%^L)1CA(<1Oqleg9bd4 zWk3K2wg7u1fTC&I@`NC)E=>%|vSgViq-Hl|l1(=IQ>?1)SFheLGb6m${++$g=iVEU zk?&KL_q~X-KWneO&$;KEdm|z<@@Vyf9^38qX!Q?Q$E%-S9no=7mwEq>?|)={bo<{r zUR{3QSZ2 z<#{VoRdc)=D`?3{`9?mfr9POuV`m)0B_`Dn?2%9MwmDWEyz`B68?pKwZ}x7hxkCwC zno#TG^#huPkG7l5+efRTZycYV{NZ+U`3LKh(=U?mU%zwo^#6P9)8F+*O}YL2_GI-2 zRsYkg4Nb$qS|gvIzHD5cVSTi4k0~A2?PW~+_#2Nuwb>l~#fyvcAKh%We{Q?od=L3Q za{Kn}V>$_|&33ceY&I)e^!y+m)+5Kh@}faXr#C76v_&i+9}5U>G}EU>C>mHb7~2P z0mZ;V1`qcMI|#{XGS}yr0RUa;n4Uac>bj5yq*whpocmoUOk~Ra3z#6rfZ=8{T>M=k z%yy?uH3Yi@2)g^iFZ|A90_8|K&F(G5PJ}zG5omUCtrV>VBh@jj{L_=u)%xsgwK_fJ zzCV8O&N*!szjbtS{2M1nr~ljPGavd*nvQ%y(>a>vdXwrI8m7nUnhUYPu)#sP2VZ~k zXV=9ew}ZyLVTQpFCMTe*73$^h+N6EpHZB<6Qsq=|q@CL0mLsllU-! zCvlXC{e4hg%oV0mLBwO&*wGs@R(2on!Gg8`$0?23q1qvJRtew?+?l6?RkEWHWBjFU z`%!!on-K6zkBdhSSM<{1U#(Bi{^yIM%|E>LnQ#Ap zI0yl9SKt>tvp>r=jqS5aTrYgm&o17)_z#{wz5GuuF30cX*xfZ zA>)?^&t)UXG{7pJ~{fO^#@n~ z!_l{Y`vd7_SsMpSK0}npWCnZ{Hw^+__uwMFA^FzhZ=C7uuY(FQSJTl*GIUaPv>6b`o!ht9tK0Mv;mMOP9UZU#+x79E z`tOfEee@)*lgM~oGB3*OA~)X8fBy3)SonB6{`%veeDt*^|Mcwi^zYGY`cJ*}_FI>< z_&4-s${G(Ey5Z;vAI?C0SeBQd<;f$pqmwfo?R1CzOWo zx@+kyaxS>CmoY}&gW9^2O*hv;1`;}7YC9;=`H|wz-zkbZ61E}?aMW- zf%WOh-@Ckg^z59A%FCeq=G>sv973SVP^swz_7;t$@wjRcxg9`Ng3YEW z(*(QFl7arG8knX54zAirDcHo(=|^|-K=P2PaAdpG%7uwY?t(2|ch)A;LoPOrNyy!I zGDUKULhA(^!Rqb@K5%?_etv&-a{LP?pZ@57gFJC9aoz(y%`C$GGJT$Y0*V&-Wf!)vffxXJ&M{GzXb-yxv;bMb|5{Q9H+_Yb}YYw5aP613;f z>e{2For?QM1#j;E@w-2Hdc6Lnlater(!#%>xAbvGFv#blu}9YD5aQ*!&@Cc$b{Z=2 zj&ULL26N)Z3=O^l`9km5YX1?MR^^#zC*OSXc8l?)Y*{kqU4PM@#3_?4#QZ*zMw zO`Ql1g$7Y+LsOh3m$-`udhHDq$(0nTBANIH+CY7I=hfFvE-o&b z;)Vk{5##ZUnE8Lbv$M1H$*o(fi_44u>h$}+ z^S=N%t~Cz6!jq@G0tX7>*ZbSAZ129idh|cO^2(i`dh@Nf@tTI-+8=$CKR2pZsc6^O z9c!uJvx8XS`_&O>4oCi+D+B~F@%I)R0FHOTZIYw43$NJQmo#J#A;9uts%&wS@V<05 ziS{*r=Jy(Fel4O~ABt-5S|@xsI(~8N`0SknWl)U9O)jv9@1a=|J8c4d#lQ1yA38pN z^zc_sKe+jijz0FWhudGKHTN_5_hpQFmo{hLB~r1r_#gcKgYR75KKYefw{HE|n{U5$ zLBBw8(kDOE2-n447jKn^()9aP;i5WOnmQ*jMAqaD0YQvuS*FF+t(Rk}KvD}eyyO$ovwUZPIVC6>hC9G=f{Q=&PbEaJJ3a86Hf{*>Il=GL){0 zKMba&7K+p>(KY;+aMEZoe2`1vxg@gJ>(I zOiW8ppoEi2Ufa6$G>cspu`zQSFw&PcAGmW}T2QFVV?i41k}DWg?2em&C!d`u5hNv@ z6l@26xab1C<7ZwG_)g%JS57Xr+b=&lzxelG`{9q_=LPsBxFL6G_qqf4WxW2N-RJGE zzWqsF{HvpnKX~xYWqpLyA$~N=K?$N9g~fBj*UI+ z21UR^x#dY$H)kPS1Fa49B1Uk*NMHIq^NSxm8e1rpUk%iN$L5RVhHtlrT;YYQly4kF zha;$IykO_Ug|M-Sjqd~=Jh-H*?8on%p8ooyFW8;HFYk3HFrFh;OV!eD!|(Qg{rA59 z9d~ctd!63x|I7m_sQ*rX=}1MhK%I}>x%dTHCn57Cg>VHgi5XzLf9xNC8nYUx@%T_x zd=oC3Wf4_mu8P2XZbLLFNk`7sX1G#Mis zH+m*gcQG=Jt1bwR)O05RD?aE+WRTEA<5sk-V zl%>Rv5W(J~pS87sbTxB&!qw1P?*VE|ty4_1_<>QJ%n?$8Eh=`kmogKmz?3T+3>#a` zTbW9n&e?`f2Z`)*=imSdwaySM_mzI3kk;Izci+7@Jv;l%=_|K>4WGcRe&H9eHg`Jp zi^23W%cv1o|I2TF`JG>R?bUmK9q;z9>D_+qMrcAuf^}LP$1E;#I>8R!K`jIGB=M4% z3RC*4j>@0%C0|kM2_rG&FA%2@Y~^rO1g^<#2vsH6p54`-lG6XtIQd2tYK!$YaiKdh zC?&99#WQ*?5kQ2fAex=AZg;`l)V11HbXnd(-)=7MeA|alo<4Z+Ki&G_kNp&F0Bic~ z0enqaE@@Es@;iXfujFKk%9?etgu|@-E6-h4o|5&C=B>WL5>|zK;%hzr+qO zn0AcG{oOG*obTb7?MfNCl0_v7e7@)skN^RbT&~oB5+nU6x`c>c`l#{RxLtLRA)#FL z;S6z(6+1ZiVXy_QaZ9WPXd0+|h~2r}7ThGZW9bB1cG2l4h)*8Th4}2%SO5B>-}=K} z#07fw`OhzYKLEAN9`-{AZ~ejjzk2WP-Cx6PKkYP~&-qJXv!o2@;};d>3HluFK9sOe znx!#>D}AnlDm8V-eJ%)3Q3ITe9Xsa@@3eaWV@p83qfmQs%cKpu@Q{K2r@kdjeH%|v zp%#e~4zG&{n_2>Adlw?k5);5KrfY5(CkK@DlTzuaby~BRD2Hh8DxHCB3)_I|MpEAe zz)rtbdvWXTy^|-89{;u5fA(Yl%l4OlnSM~{XZSldEUwpj`koUuTIjg#-#+^4qd#-G z-Tq(n=EJu=J%5U?>#6P@b{(ufezqVkv>!X_!$Iu(q+Ak1n9}D?K*!|$ai0sqRaAwl zp|MkLwp9*SMd11>8xyO|r>bM%RfU?=zY0?prpBmWVo0g*Qya?S#qXRV)F%hIBPa&Z0{eT6~?5RDt_dn9#jnjKPzGS(HUD3toJ|?@1-j zH-(DEczIjL&+ivRaPxY2EgC zeY!n9T|d0s9REAFf8eA4hWLjs1*Ucf1Z}sEpZs0=1)qE8m*@OuKwAKSf^GAO7zw+*1y?yJ}uid9_DIp?!VP`l(<-yt)GdAVM#>nCHdH+kIWLc$|E$Aov>y&?`Nae^~%v0HwhobFPAno<`j?gtkw z#7Z~6#7yBeP#ezQ;c^~H#~sMEh9DB%7x1y@(if)kr@%rsMI|sXSGvJ`J958P5p+&O zx?^ErzkupD1K|Uf)-e5~;H&E==k%$-58zXQ{;v-U3uloh{ehpO)t6SM-@1MOU!0xZ z`o4#c9^&d>-7Vy<2Z~C}xF(^aGtSMk!ftWn{1b_GOCdx{W9D#;Uk$*bZ;k?1dIFAd{GT02`@mSsNhnM0i-TV?0@d7OP=+lMkTvJ%BwZbB=E;}w; z>QElBgxWn=J=#NUikA#(1CTQZot>noEbgajg3O5+>qg}kojF)6e#FzC*x1~@edmb& zeBQsledl9;ZuP0vIR(? zS4ynm8CQE-9e8^bFZkyk1^qHg?Km}K>JS&2jy5p~F^)?xQl@zf-SSPxwfH<`jTiZ+ zu-TV$s*jq)!DjI>9wOf*u}~7bq;`D?P;!tRET&+k z-N%m~Zf?K&>i3_YfBkP_O%rjoj`7!b!a=tUs0rWXbNAx>;%`5ENPn6ON5Cu>b1%jm z88^)jY{7#wKBi;$^pvz(G61UiZzF|;B5{yc6|cQyhb1a_*;7Pw+XlO=l1?K|E_?n?G%zW z01D?H`g!Lo@BHisUVH5mkLV9}(DJV^6V+J$#qClq;0hK!NWEB`@BIQSW7Y5RoWCD6 zw07urIzJjeAJa)x6_q%q;OuJw9AEpO(^Vc<;q=fee<^{0^uN@=MCuk_!Hy(-sWNiI z&a{!c560SkLVv#e&b`+@asJj;D$7DH|v^F>B_jOj6PabbCwwFJTwN0`3;$Cx?-GKgl7vA~b(rf>pc=g_^ zpT--11l;3?esblWDOH^@$(0nrfuqlpz?Fj7T^|+23!yF@tLrRwX$=c;3t>C=sLR_m zcIOk%u?o%$+zNq@ulgjQ)uY)2$km&#nZLvv&wM+G8JLxC{g~GBoxArw{p9i+KY=xl zwQjlm4&Za2`y79zH@)`%Ir{TNcZgtZobVK^aDWWM~pH1cm`Id%;MPA+v4BXM5NMnML9C)r-Em}Ftk&z3zx2+({PGXd z+Qu9Gy!QF&_19%1px+|;8~5l72g!MP)pOX7Up~S5@zyc$Ea$SQ!?c$BI`dyA_hCli z`HlZo>epE{J-I@H0PKAhmTUo=UI2{jA2H!>p51-rh$Z_ zwkO~G^8MeUuNeC5<464Gx)~s~Et!xR6T7`DM;j{)%XuRHigNDO1X)$}(zpg!87kEZ zC_flu7H!jQY49me^O`n5cu_kpIBHJEsGv>U6iR(9hDR#Ks4OE$iu+Ily({i|_T|^! z;&ToO_4!yZza){`MCM=;9mj_DiP6<&P+mV1qZCDf#1v#^BHc3wpKt=9M#ecn z&7uou>l0qig1npCx9%L%U+Vh8?emZQ@ajia7yR=Aa{Is9-v7#@zd(Pz_cMbN*NE z-nm0-|ERkCZ_4iEpTjKkkNZ3qUzY^@(nlS82;Fk;!nM4QljAgY?o!O2*gcc)8P+RO zMsWclLn2OOj<0>B%g@lty?~ruT+PWLVOomld zHd-?=L{y5hB>T|10{>i^@ zef{w#sbfD#Kluli9dG~pC6sdJ`~}%cOJeh)Z#m{I7%q7r-;$j3oMXhZ$T7~IW?C~( z$k5op)iq8fnHa^IYfc}hm#w}kqo!lGT)CTWFg64Poo4}bzP%5aDjkoF-U*bML--U@ z>)^GSmVgaA8ywqrPg(;#)pz{l@ncGT^nE)15D~u1jn`;l_h@B0p8MPJ^F8^P454o7Zq zVkf!km|gW&ImF ztWEJ{WUWcAJ>qvSvQE8+{;ZRRd_mm3@Dl8^qL`Sjx-xl+_GN$srZeVT^%d3~x~QQS z8;iaXn6J||3m+Q<sa)2H~s#y^j> zfBeoLzw=?*4Zim&-W&+!Ue-R-?(WT~?9gG@fNKi&66(T1@p~ z0859)Bc_vmalwUCLP+nJ5}o^12s+mlbdMvO%no;YI?e}k@8JXx&^H1l3RS3)F)6x) z&gq(fVTzShoKL+~&g^mwp)P0xpqKmKyZW6!{_yd3v;JQCt-lY`H;2$23~zS^p7b)GbcVz;UjcM`~S;ujBXDJA*V^MJHx?{t}$?6gzWO>NLNC>zIM=c`N_y zU4Y|7D0p=QKj(nXHWx%E)jsxf!(<0#(Q{0o@>sP=zo5{ejR{Syf|AWd<=FTbMI(`B zyC)G8EpM#xrx)k=+kziH$J(dA?f2PRXSddLJHWpTRQNJgu`iXT;-`Eg{;8NL--yrb zh+P(64W|)2zjp6}LSPWRBR!itvoJYwlRUL^4?Tpmx5?rx^(ok6!eR+FTaesKvSVS& z4ADi-i>#6n?;JKhYS2dN$TkaVY6JyAQ=d3g@l{G2@PAY2c`H7Q@7S3Cn11hkXsfq40LE?0@vm?ffP(% zm;SUtrJpI0kmQ?tdhx%emjT;PAFq$spP*k2!mWX3TiWDwOQWW7BhFpzB@24Byl104 znXhQ?RjG_v9+6__o>%Ik(MU0*)Vh*L-sB{Y=%pt0YvWhU6h;dDFB^wC(WnRkvCM0B z_C;lZ93bFIeJ~&Q4~qIlBuxcvyf2KzQ!hrH&e7M)c!^(@I$4j8K5ypn_Fq=C7g6PS5Hg{$rO;(RnftpgQJDs5T z++=gTET9xdO09=bucd>*43~UxDjXH3A8PD}lW-uPdwE;oPrm|hNignl-dVHSS9Z7y|5u3WxF^m$|- z@B1V&4X=@fb}BZ&4k`9KSTaocyS}cm@R77vFPV0pJ0pmAkUu#>@b6=I5-Lgq}I_rlSdN1wi2Z1hl&96R0KN7UPL%071KTx|5 z-##&Qxw~-eg3Wo#Y035%OPjAUjX)1RwDlaJ_cLxrCQLnvl}j;FCn;47Pnwz3g`tpb|5v;_qpC4g%%HTg(UJ8DcDDD3Ryfd1yGn0yY|C_(g`$41S%A>e&K z`hVwyem3|XTojt46~Cm}J8%j43y4XB{+#PWEyMB0$)4TZ5k2Zr@lEFu3QXd5DJzGT z0!=EebCL(&{NZMeW4xHb-os#`moE(oigI~fi4qD90+HxxS>R0KPS1`CR^urqr6>lL zDx9kA!Uwou%OWl|aaSWt^LqgFEkXBAQZ|5I(!;yf)Q_PUdmH$n4M^Ra zH+7yl+?0RHSLcBq@bjY+z=>;QS5R^%r2x``*F~P7qy&uAnr(J&`4`%KoRuVS*+RpQ z!t;@^T9R%C(Ae!5^Mi2WXxxp#$|e5F=c)_g3SMme#owu^+7LW(fa325VFP$i95`2D zm_ncW)&Q6nLD5Wx`LJb(QGLr3j{A-ZmK3j;9yHiIPq29()#g`QcLaejxFG@5kHm$m zTD>#~FuMtWjbari{FN0YZJKH3l{qqsq1=Po1D(vi#2EJ{Q;}SN?31dg?1b z;2FnXRN@I#P>6R`V-EbwKXuj@S9E+&yUyu%SpGAV+g z<_L9`2?S6-#wU4lt3plTNC3@>gKGJfI!_>U6##EM$ zd@nTNB0yt?e!`OPAbslPU|Z#I)diVUUbfnUI|53r1V|VVTy+x|#0z!GXA9F8Cu)fg z#4I^tjP|GD22aFQ9p{D5u}WZh)mFw#d3x~{V{P+N0FJ_cO)mkEAeC$7?9ndgLdMdy zsZ>*_fltf0TCWD&Q>7-K7LjZV6b_q?m9d+~Sf+NI+qf2E(S+4o^@1rjL*Y#3*Y)sZ zoY!34<_m|!a}7o3da+1&bslKSFk5<1xKT|=m+ z#8Yrc<97g&|9a(+{Yr`YJaQ8IK1o*%=FT-taZi!$8&`9JXTU|~a-!=v=v7>$N-P;G zJ2SDZI2c>hk}TMKK?R!t$)L7UZ?Q$P#V-dysGKn#MsRd_ECzA*D8X)wSMr`b2rg!> zt4)Bub6tFuW>Cq_aEU2nq$O1ymA+N+);HvER>D-UYq|sQj6(rMRka)h3liyY{wSvm z!Y~avrYrIZJw?&@loQR8BIa=JccIBRJ+|XG5J~J5rt-P!f()bf6=TL*Zg5M%WY9Z- z(t*CMe2eq4saV-_KD_ zys{@)P=c81=uEbfN`jgk1(2wG<7{$~f&OP6u_Xcm%}sRR%en4x06}&<=)c_+G`S5E zTMdh*B!fB`EPx$emt5^5R{Z|VVG^6_a=Rm0m>ZMAK?LAP2R4|a)pNT8NVT|vDofdJ zR%_D-28?53{IuL>wiu~s8e`bf?usvwvr^OLU=aFL4Wu!6aEPmHWz=g&6(jkZ6l@N) zDNFzLJ~_jNP`D_*rQq{mMIG!807*N!EKj%X$AG1DAqI}GB0yR8er)6^*leveVKUy@ zJJNT>k9cP6!(k#vwaEJ8Z2UtaJ`22n>hsYFX>%y3!tTBaP@T;tgqH;9W__+INa$^%fuI05Y$tr9Md*J0Q#4jnJ#(0j}q57PkVHw2ZmQ8_t=+N=5{FSOJ zbi4_KOb}%gfY3RQnBZ#Xnk_ci%_QyAMgRoypzX~00te!#HD#`^_77Z z0>&mI+vTN0ZHG(wMt1`Qu{#Bb+L{djV|bsp>K?Me4`x+r$0hY)n#BO}Bd7 z64CSY`_-{_^9V0raY*d*H-S!W{piOTOyaxP0X5#}V$R-`9TT0&>h}gHpDlu39LgcT z)q%}|b6A<(RuS{SZuFdyYq6TAz!k>(z%f+Zpg!(l*Oz0!*K7c7Jz&bIcgM_eh#y%X zwZ~k19noj0QM>5_&WY0EsF|GRs2RDCq@>bfj8xIVc1%vv^{J5>NZsBsL~%|=f|^QP zFhm^7w<)kx(f4oO-_}uSAFsfBKEu~}4j~E;L z81FJg(0_|}bBJ+HclL$E5%c%q9jnw59f=c;N#0!-zx1rT0U&YiIJBl5loXCR%$y%o zg0W1{+G7b%8?~FIpLxhZ@Gxfrj82M8Qfm1hTKaR$W=EhxLfk;Kfl+r;W#w;Jx_&ik z({O@>eHLu3*(FPfBY%L1H$1Bk_!1*?8-Jo6-%c%WACOl8%^QzKXA6QkF}me&Zo|N} zbf7~_J0DsbZ~W>n+~mgEX`71>zwkhmM9e!wmodK5$MmCm^Dv0=ngCF)eGs zM7Fk`Xo<*{acqekV^s9CnTP{4!NH54^bO5yEdGo0i`CgHw^sK)^4jY3_9<}+-c0wXjy-MPmFoYPR z+`lF6iav71nu>ioMsT_f4!K(n$)y2R+;9UZMVW-ie%Xc?wH8(79>LY#<})4uTCUm> z(Uz*ln0>nKF-gQUA0}>G{2%)6Kh-OII{Ev>OIOB|+h?m+Kk<>(5#1d<{>J^)>Wrr6 z(PnkLrJq>0p{7>OhrLu7oE^qHm_|`>HQH!-m@VcS#=Op3QfLLKkm4=U)D<77mZ#)w zea1Vv0e~Ra1+CB;Np8m&iyXeD%S5Ejb6H5P6V}#Z*imcsjr?Mt;MH<*^~VEOf874# z_`r95uykjFLUO!MpLN2$^4%X=J-PpIwK=CN|B2iIVA}D^f-p4vF(#d7dCy>Q_I#{W zU|7KxDG&$I!-K6S&hg|JVqHp1K3kXza6KU(=N+TPgG~<$e`@h- z-BtNkua86JH-4)i%u1QC=BQiq9^&e4++`CelKI*eJ;7m>h<3ftmUE9RR3_gE&^!Kk zcl+j8Is!PqG+y*ZDgD)|)f#tGSO|RHG5c&RJM(~CViiG<9b7EM=$tn?NuCLJwDY)S zM?tHdU)+(?)`ydKWTvgkYf%%)?7?jybp{8M(re zd?t$mJNVMm;0$6RF2=a<%^x-Zm;f%+2I{v38zV}(7*(**+R7GP7>iWUhAI|FeQU;C zw-#$4*{=N8o%TJ+FIy;vP~QB**yj6AA8ixT4DZbx9Vna{A2SOkgBpWHQ3dT`Nyrh7 z&gK*`Rzgq^4e#7>zISkfFOD=c>=a+M0l))CNyFITQ;V%5O9?G@Nb8-e*(9QG6<^6W zX1|Ij=bq0ddq9r5O8)mxYLT9y7j;Ej#a*c& z&-DaA5OWd5(F_9vi@&o0WU6@$WVyr`lRbC~CtD)6m95a_G0Tx{wt0)yGe#mm2AyBG zD(re|8rSlk^I-V#-~lb?qHG1?=6)Bq3`4ULp4VBa3aeBpR!epzfP(057mBm#E@O({ z)WHS-;4TBS9ADn=FZ@+m-kl;CqQn&6bxW3ehdj@t%pJ;m8K9UaWv)~e$5X;%T@2mL z0O19qWMt@`r>?OoV2Ro!?9`BXa^@<&QgdW+E?W2q){qlRS}2Sf7W0~Is@)&o%hJ|^ z+|k;LnyPQ9FXmVpbAvnh2=M3SI2ZM!$`nr*Pl;lPn*pMVM>!fpK=l?BqyVW^g^1D-U^GV}nJev-NG^q)|3UXWdZ zu%G6=Z>1so<{!SbI)8Y+x^?>&k0Jm3AKebv?*haHuy`}T$n{PKpRbAq&l6I(d26#@^PPx5NPZn zzY5e4+!#6|a6#V(aQ~}suO85^1fQLq(SkqLM?2mY9v4Ns830wEREyMhOr3!vnx*lX zt)sO>oU%2C*p9Pf>MeZRAa>XQkk{D5Xaq4-R*8rk1IHf8C(mP4mF#e;r?h?_j(Qg+ z{3P60zVt`*odTEqMIu}cu82!XydNSAg42vmIG?TEio7u z*2%*qV>TI)hc%asK-}sKFkVjd13zcU!{8nK%`l}niFI{W+so`HGE!nbi)z5z62gY1p5Uw^V%)%(Oo zBI6O8u(_@T#GK zL_-;U-UR3>FW(D@mU^iD^fepFBaH))GdZ*fvc_1qQjP>4fC7GBag=||H>hP`?k;+e zck#tYKL-FMjY6quU@ib7ngx~+k|b)s*hJJQ#@RTAhTSS3()7o94ByFvUg2AE=&pFR zrl;d$`c6U4NFH7!3+cGCcq-#mZz0ef#<<=HX?$~aH!K#m$&@Y3{`%b@W}-fz8@wHm zfSY|h%LahH0af^5VG|4Vlw4Yy`LffkCWO+=^qktd?oh^rYu|-$?0N?f1~?6-!RFc! zKL;g$PGP;vR--Ym=`U())uWXMmH?_hL=Uh)Xj$Mj@tSUtv0!j}yf3#E+ZoJKt%4@7 zDco@`xVvzYDY)4loZKVq1aw&6Y!iqRJ#H=v=Ek2wj{f*3fQLOhA#}QzbjBUWB83m* zS_h8+nafK6-wO8~97}T5cyy_76e)GJt5^kH=d~F}S*3D3@6v}}nTVHn#J98u(3KHy zknxog_W^sdFAAfnwg^{uayn?lscWByz!p9$6Arv3Y6SU{X%OjN}gCw%IX1#dWznM`8hMo>BG zg|TQuu<($=Owz`L*>fdr2im${P2M5b(9wAxguaNiA!dV-lo2XYKccm^9Ke{h#g8m? z#O{@hm|>r1tMl*Ehp7s7^ZDY(CcwJ_#iK;+NUk<&ot~Pz<7%oREZ>$hb|wJ42{Mt0 zc@1wkWN%1d%!8kDh`U}m-3>vYnagawwv#8pV%f>DT&e5sMh^1~9o)yZ{DSvi2I?mC z<=Rs8@ja_06OJL%(2+TL5@wTXfxuFBaZ|=+fgw{vHsx!;hcuvcN*?p)-k25A%R)Ty(7eoTexe`*eW?Y&pnC`Y^iC{Vh7o5-Z7xL3FG2g z{NN6t?g|3)NvjdFda7uDu5o1OTmS`YDY0uT?yE{jUq7zOxlgKdvydIQ67lXzx0IfdO0ueU&vLyHDA!81wwuv9P=TPDE5}tl)mXkrVioj)@39u6zMk0};c_n}DIGdIf=%L6E z<2*?)0E{J;rQl+*xD!KBLO?O9G#lJROdFbiZ6nqVlAz@*2UYEQJGR(2C*Us^v)~;iE!x6d~m{cZ!P$a~Lq43pd7y za=O%QD4{Y!gqVlKfw@m?))LQB zA&ANyQQ|TH;K6I=VDgknFrXQ8K+|P~?Y1!(_to1`JuP$HuNILkU3f9(Q7MtM0Vt@t zRU8GB(4|r-BAwu+;WM~h>x|XRP*1wMd1182Wc2>dm>2R?e+=sw2xjCG+>;yziC%`| z$&3bS956J_Zd^nOt&sr zq0}3&1?4P&XjZSvg$1!!fP^ub%NU@0B?Lb|=&9OM_u}gXp+@V+l6n)?0?gn5JMbn|ZP?;cbCAmovfD$lK(l%w&m9RH zgPXv}7C!DJT=a058H{{}%iR_SLlqlUfepaX+~M(DL!l-Z0-uj#vb^k!W7aJ;!wDjW z_C0_mx|p2@rQ)w zHv_AcHv&^8Ye7~3rHvt8j|tJ)E9l#=Dz!NZ%CI=I@7B(N~Ddx_Ut!H^9f~*+G=oS}p&RPRc zeANC!=u+zxlWYcU$!#Poem4Nh77`K}qqPzcL)c^P=m;!XG_Y{7uVczF{W9A-1dHkv z5_1^rnYaBSI@OLV>r!-$4aeX;fPfoRlFfCp>z=21Qda_Xv7vB)lf6qkEAxW?y|e z_s*GzbVJ9m@bP;9TGh_QajLqI{aA)oU(qpDO*`DYUhOB*_7DeZmfya7<9S1wjlXP&CiX7$Y0yBEN-x-d_Z>lXZ?a2-3u z+hac4as)Ux#LR0RPsbaUYlbDY&SQ`*ELd8)93y7wppsl^Gcdlo6GOeOTnTD3;&cA$zf17{|%QgW^p0o0HpeAni9=FUZ z*($Og#L9|o6&78S@>2eWWbeR+33DQsKjx+lpms`VCTMf8r331i2En_SOO1X0zST06 zXtPYTMCEK6xFlX;GPTv+%QPtI5Gyz%bp@q;I; zlhYG=8A5jDwO-Aoh^aa!BQ?4xwa8CN2fpLg)uRVb zSDVWX{k5XC_fc;K@{Q%*1CU4c*=z_e=!u}AXooS6&pws=9Y7OI+1i8=)g~AGbd)nJ zggJgfn4}72262K-Ts3DCYXEhaYVB--mldG>#b56|yL-BNeE$jkp%VP5GF$UDQeh-I zrv18aL=s(Z2(U6Dq~0&jbB>};^t+Jzf8{EPE^Ppuw4b@jHHKA1I?#oN>sk#VZ4hm? zdg`ibx0{e+$>{j#P$Nw7VI1$77M-KM(%&lO_Wk2(cTI05z=l`P&9$#%10y= zvficuk0TF~_sWHPLF00~;sYcL6cufG2{5jNqi5VDLj^2qmAOQaR8Yx$1!XE5(^e_% z2P0z2DUK>!CK!zNP@+nW^e?|m?+R|{XA*b=pl{gV-#!?AA`nNDMx{~@HyDw)uOcB~&>(w7yB0Z$8A3gZ9b<1KtJZ zlsYLXIK2dbXUX{EydUCNPBZR+)lm@3SVt)5i2yZ56Y@INil7!4dlVr6-b14Flb(4a zppI1o0zZz*EXMR45OFgA#z+$Wx|C4Ib)~pY-c){4hFN(7STe@DF8U>$O;vjZ2@8jE zpT`{^drBpB3H))zR7pM^2riL><6773P34^epS8<%? zBP&E2d`%V!7vz6wA=OYwKf1%#@u^h*D zkiAVI64c3}?^(&6vP|FiJ5wNA70z9H$O2TLV?VVH{rQHW4MntA%BQp~*w?QmRas|{( zeLFJ8vDz`T{TOhHjD1spkbN39F#z4bt}$dIv7(%csG!bM$E0}@f+8-M6OU~K5$k}L zU7${HHk;L>cc0Ra0rT%9mhs1cG=mZiG$K0=N)H|xcOTBHMem;Pa1p(+*qPnCPu~ZC z;3-n~xzJBH<1Rq~7{uc5N?vi-%SI&=WQRFWbiD1HCDMc=#VK6Km|!j?fg};J>!p8s z^DEz6U7T-Lx6V#*13+U#PyB1j^(fR`WpEypJ;tXOGMBlorB4C87#B)R$5fm_+IPV(X!dtNa9^cg zmJB~}_xoS`DqV3e>B~UmH8*^?>UvriEUIl2Bq=QxmI&bmPPhF}X#=3I09c)!opCM4 zYpDX*-8X@lSF>>wMlFaDy{o(nP3-rsd4S&~*FOB+m{fF9x^Oe*mjG4em1;4mUAo|x za3$g$lqKAwAxW{MIL?K9LW{Xb%#nytJp2W-Lw6b|Hh_2TKU$qXxm=x`;Fg;f9qzm7 zNEw0>#Kh-z7sOqu9>cjf4}JR&UG0x4|N4}E`w!npM6dtxCLq_M^>`o&7?Kfh#I}=W<6Cbmx>zTeEM0A{vUJvKze+Bh7Cw1p zmV@bJkPs1Mvo#x`Zbq#CW1T1is>8;jQTktgpH4>Ky63 z4j0B#h9Ln5o=R0}EPnX3MShjpfwl!2A z%JTe=0wYCd5XCeuko}#w>56v_n5L^s5AU=;+rEPQ^Jflx5Uu8BheJ?8v|yLwwykyKPM zI3`?!h-K!KzYdvn*5GT=)0=?EAmgBgRfAHgO+oCR19^@noD5eTkeqyqt*(`^RX}8m zt{7V{Ao%Rj>*4-3tayCVhK+z^de|L6tj+k{=E8V%84_>}hZ(4~oG@V|wsMELkTC;u1})Vh*>J40BZkB@;fJ5y z%-9gnTi5{S4044xl2MHLuhPmbHf2W=yaz!4_=~~0p!!#qLY-B?mC7Ztt>dUo@ixB~ zgW)TiLKw`@akJLk0rG;0FHfcW3|x5$U}r{IDm8JlZvYsWQ@knp%`&qtXlIh7QmxdrD1$;C?#N8iCnQ0r=E)&R@sGHM-q}>CzX%rb#NVO}om>D(4GH8CwMece=pI zMj+sLSAb=Lm_$Q{$kVH3A6w)!Ip&2;D^z-|tvJ|Tsm0cDbWF;Kw}TjK?cLZ z7dafWL;z2PGcM*0B&%Xu#fODq`?Dc4=ae;hzsVOMdyJ)g=phE zuDdY6upd2v&|-G6F~KOnol}C1=(v`t=o{bFMqrOO0ehdKOA&GkNp>L@H!x>Vr6IEnwZ|LUDFX#aT&Hi>2nN z^+?5&pP!=Drg^rdkNb$!dFivYNMZJ5gOLo$MkbgI55eV}`bru&Ij;+u&dn}J9d)hz z5hAZ}00&3lFhs{BuA!sNZ(O;x3Ok>wWZ;dq?+8>odU_KOoH7;YM2Ns?g6L`4)^T)9 z9s^+%mq4ljYf~cnEyvJjfDlymn>`VhxMrqDjBUT zVo4GE0Dshb`541bQfM!xV0sMM){D=GrJ7WZZD6hsOBvs{xZcDMgW(&14>iuB9zkS_ z>n;eeQZ2m9`w_sA<25oB+VhPjOnrw@k4xlA9OU4!A!D)26$<6M4LDb%W05m-b#D=T zT}~J&w>HktdN2mjF-1V}{VQ)Tu4e-m#Ef7{B|Ya4Luzp*qUFdF;@C2QkyZq8LP24fHz#Ck9WvExU7fXGM90dLJ?5hMFljKz*x z=Cl)AgAP5|HUEpCkNSSa+J>J3V8G)%V7*FKW6a&dCZ7Xj@tDtAxceSMlor>k8XM<> zrK-oz?8f9AMjz6$9*jYRd)Dt5 zMwh3W$2>R(as14Vb`%paJISL&oDA7sf@7)=*I0h@DS!``3a*DD*j70z)zQ2>J5HG5 zKSyu^vN4vo|<0u&u(#wnRqrmtw9YaW32m)EyvJ z_^i$=C1I8WycD=v5pDE79qf!#OB^hI(t~Xuao4>2HxgC=Y!XFv?^34${WSa1g>5n}jz2 z46M(VEOPbi4;a_kD^t&HF0eDwilOsdZ@LvlC!#ggG4*+D{9#d+{B+&6l|(5l#!X~k z8yU=&FzPoOs*G}5^>WLGibS2*xcEV#n+?@VUk+4fK~DR45Z-vyKGp*u`=PZ*9Rozq z#*sru%L_~V(vd#m#(KyRke2}AJO`2Uu&&19v+1u>uQ*D~pvuTsL*OgS#aq#{qsG;- zWl<{+1;m)hS?qL0H3zLAv%ASkA_L!rn@%k-jE4Zf+Ne3uAy;F*bD=Nh-(sgi68y4( zit)t|T(${7j)&LDLLL|3)T|+891;g7h?Y7|e3>ibp*7!X7KS*HMzPPXUfoeDuLo`}xK~jo5l1 z)|fbkO51IKt792R#!K?RhsADRb$&!+kAJe|iLXn1W4!VRG;acek*AQ(qk8|3KF3yN z)R^d2+Gt`K_BctVIAU;|gpniWF&X@Z@hd&@hQtxedC0(HKp279v{aq_ATht>Fdnof zGvA(1ckZ07F6eBXoXVGh;(Pf~xYQ*1K`oY&k^KsCtkzKLYUU=!S1RdhVDn=I0o5P*Gl2$jCOrFw7U7Yy^gf6E6$m!FW8e5Iwue_=`NB z`o53u7WA^AX^hUFUaa1E^U>26p4a+uC8+F1hra7oekt(o+mBZ#XY>{a4Pg29GC4z8PH*$8~_+h18de(+?qx!BO(BGmT+XfWfOf;gA)94=$NLDeI0%rl)Q z?0E5GBI98P>%kaAgYaVG1;>F{B;dD4%V&Xzi4Uu<*G3%YQW8$dvgC`odwI-;Huc47 z34q40F(rqB12AqPf$B!=jR1`s7W%DM&Q=$fmxRF9iov`&u44q~B|u*INW#OdV;+Y- z+8r0F%6_{t20$hD7p;IS@6_U{F+INeDiL4aScM)(2FsVYNnyS6Ggg7}vH-Q`@id;Q z>o@9P1TbD0SNaQR^m7jMai_cn@J-<6E?_Wldz#Bj03zVY8fI(|lP}tlx5U}p2E`3Q;_QV4Y4V>7s%v6h{l+2jT0eXi#QAk2fZc#v z!%Z_k0{#O}GSCv>1At|u2p0Y?m& zY!N~>pT(GNt>xp^jDbT;wh@3(Z#pTU=5&tA$2cMi^vFPun`7S*i1Th=j+?mY5h!;6 zI&`&sPS;b7;F&G!Eg4cvt1pwnHUaHT$gSH z=C>T;BVH0%)*Nn6ISqm6n`4&|kh=hiz{wsABfAE^U2?r&v5S5Zuq+H+OyOQP`Y?#~ z!mRx^(1weC$nji8pT+fBi;3|yzb#_p>|qUq9O4&d{_$H9n>++!KJ~F|!RBKwi-hy$ zSTX`WmP^1ln7v29Ujjr19(+9m@kEGSCUdkS))t)cL_cIi)tC|1?6Hk}u*W48eI`dg z@G-Y7^criJ)=z8%<`)Jef;+{+7wqQCAXIU4+>F4BH3H=&fYxP;0IE-9$b+jsV*)UU z%xHYk9%Tk;F23<5XU}gqYkQa@`t29k;{r9j*|^o$=d0Ldy0`W+M9~MUEh0lSep*6tF>;K7 zwY3;qY|(8#*vxO6l=+xn7pvLO7Q8Up`=P+vXG3j#L*%I7a44^)tG{HdJ!}Raq+=5 zXb>6|Q`^|cAa@Be&0-wa!+uC|L_g>5+RFw5Jns}N2>4`uyn5%s#p>x(`o&iaLGUTv zO%q2z$LQfZPgZLhJKuRDrX$~sYiCdvnB+%+2^i<4EPjbLJ6aoLmIFE461Ngn)dnda*I1t{5JOXch?OUs-k1uEgz^?^k7STT%)A&<>H$!!p5hyPKly9{7 zZMMgG%d#4xT^4>WF!~|m&e?2ri<54)%+l9@$AK7+CB-;wJ(|v7dm^JvEPHXld>UWJ zTHVnuc-rjr{RU9;%N>9=N2{~5lhvQR@q{*=w^kqj_-m`XcWKx;$cngWF#;EKm-N;f z_gCL~;{lD?Em`#RtI&3SZbs?wBjBF`$l{<`sT1E)v0a?Lpx~>o>Vn4=-y)^0RQE@F zlo1v6DBGH;ZBaKFSjgO}k!5;9i^H%c$V9VkAjDQN%tOJ$O@JIc4%8kW(>M6g&$OS> zue!eZ=F`<%Z@sa4i{J3VWRu;roCj9mxdIaESc~e}qYd%s2Dcgnf7C(FHW;fz>cKz5!s3v6F8I zYg-#MWU#z;7ht;AM22abawy2K+?oP1I$R||Wb{O3bvlMUh7+anxgqdq92y%hGdY}{ zV#U{+*&@ex9%3`#jgw@vTl`IX*bz99g}_tKnfX-8`Amz)6m=1Py!dT_8L7pftrtDp z$S#;NMJ&cRtnJywCJO;FSlFgun~19VLf{1qVhV^p8yCdcivs2(P~;lfYT8t(YMjbsc@)hYT-vbLUDS9qHI8H z@9=_qbTAJ+(RIU8Xlv}HE?|*ZQk=OgqAcD&+$A=z;0YR0_{rxT^ZWQR82OWs>vp77<@Z|IM@_y%2Nb^ z6W&O~#acZLfHxb*1`gQey#PT7mQmgx_Ry2~IEB}VxhH7JA(ILbdu#$k#*sQRRg;AD z44+JXH<699lLJ0GSPnM#xh*U{w@EZ(H9#BTcANE9bGqHKkL^I{S|v$%EQduGzgTKN ztULw*Xd3_+dw5~GdA{lhlurT59Pr7{CbdgLQ*#kc%OY%mlN?TEam?9_ z*>NO5WIv*v4#ycZfrz=BJ&AKOT&eyXvKtCl#V*$%CfY zh%o5jXP?`kEx!w}SmBY_$muc!Ix=2t_8fhVH7B*9>uhCw@I%CcuW=RRM!kj+D4zl- zxh)26Rgq?}_$`3qgXkMYvFT|iL$PHUC72H_-Yc*ph-`|J2v_@fa4Z=-h6I)wCt%+Q z$YEx~L)Qy7)52bCEHPiPF|61WFIIZCO~4HR$TEisJbaK~GPku3P;JuyX~1b6YPmF= zQ@NbtHKwvzKwO{-VHeB(l(zZGFHAAkxNq9$GXnfckX_Yn0dT8|G&?MQ3NXHMy6Kdw zU1`~sWGIF)SxmgJgsVI&M0|rdpn>zlaAd$VwW-Qn0cc|EN4Cin&Nj~?{ZQ2~u@JV= zlliQ5gVqP#eAa^2WNuA~`6g_YOQT8?IU8(akhp=lkXF*J_$qu>jpuC^{5}U9+hC=W zqKerJgR!b@(4)=exWLF*>>@*FUij3>HYCx*^tsmXXqk$hjN5E%H=q$RzFd#$7>)&( z#(B#^wFy)h58@nf+C7H?(e${3+eFW+F=qER@VJcxi;K1>vtL2B8Akt<+;9g&5SgOZ zr+9CC&vpcwAG3;!5v9b5VJEW2FwUGYhHQovEaP^bH}0ISdHk)IC`&9Lt42t4*Pz}BrbRSs4wTPG!(7QzPM zVN3>yp?Q#aU>?%kIF^rqyaZ6Z4YalVicl4^*`zidq0wd=25g2w%MaMh*vZL0#c(`i zi|w?aaGU`afr*(Xb2efa! z4aZpQpp7pD7-E5nM{+Y+Yp&SN`YHgcl`ZCvt|-TxQEo875X6`whTq69zzEE|1ZXR? z6I&LBPJVK@Ps4zLEj?g^fseB2p)yXv92>A|?sEla5$&!)^TDAxMC#jfZGtqLQh5DY zmvP_-b9f>XN^6i&Tab{5n}7*xN(MTnGB@fVJmVA;u~E+8sD-hJ<|XnV!N9zP@dh(7 z0`e&UjcyrWnQVwL+p+~9S?ZXS+nkute8rX=%ul{LPmD!qd<=aCQb2j>ApFP?A&{-3 zEJIbnURp53ZO|fSKmAa50g_$2U>#s%S%k@K0in(Q5*Hv@%mMBjnB8yVY=$1bXEXdw zd-xHkUIL77{q4k*#erhVW&y3nxQ!gP@al!1`4AyI;PQtG%wcl$t4s|a46$Ih$)@7~ z5&*&Bc$L|v&bSG{K7GjIG2Urx&JV1`S)1eY!ek6R^|2CZn8?$!v0sU%_#)4Z{N5OW z^b%myC7%>704a;#Ox~)1hDCZ3Q@JIF!4|&d1co0j2*x%JnC&oJ+7QSXLW^uh3Wn$# zkSJscwCy7=;b}0z5JXO2 z>-*Gn6LZ)R2zLPEtG}hR6KGe(q6?y)@MXec@#{f?%VdJg57_ZAUExa_#NgokMgToN zNHKoiyzml40ciYC#cYfr=ThBx3~&0bt;N@Ek0)VFVPM#dI)MqA{3Xsh9=YoQ*o0yzq8P?&j3l?kEX2{z zyojqGh}Q?AvQJmQDtaZU|GFm8T`B~zMUi(d#ao~{nl(?d!BP-TD^<98FdO{~Z z@zsMw>#mD0)?x z&rV{kOU%ULuSgMZS9@?KUNxA#+Zu*i^IHvk_h5eIhH6PzhH3(QbeQgD7sSsPyJPD3 z3?)1|7Cl}A_S%}aqnpXv9*x-;vjCVOVdKaSqg~Jjjs@r&$g>*({>CM&eOeH29n-he z@`TkBZRfizhW11jpA_9pcw(UWtM-E4M?Z$5S?7B;slSFbUQ)sNO~_EN$O%4xMb zpT@`?go4y*q28)sI$$Onuj|UuYev{QcBJ<>Yn#Q^cIfQu0o2xWH9U(zP1G|r*f>Aa zq%U}~Ee#63nVQ&Z!M#*RMwNB`g`;&RHapEjISyNpTU&X4v(C zuhquGbP^M{>FiHF+Q1Np{B{JL?d}X$ZN*xDEL2*-na{%`NR3@z#6jNaWk4OAbjVRV;wuyEs$CR(89AQWHI=-j(Vtck;?2T>r5x`3T z{4th`&Fc4$PuA-%;!m-Yt-1v$3nQlJOM@JEGe2L=aPmMgnC3l2PrwV2Qf38F%+Q1x&g7Rr?sZu3QKZ@kQ3$5{N*Hu8*Q z<6?hOMiJ2-`qzo=m`ZSP2C?{Wq~|#TN0+qdZ=Ife@tA}!JbiM0Nw)?5W5G6Q?M=E- zrGnE*qG#DowzV8fifs}_7golxKjvym@HRfkXfSe$b8WnIfL zf3(3Rc8wv${M=SL@+E)ZHZC?9C>a%8HP$yHwgn9eqcNL>MxSBKm)c2Z&6*-5_hh`{ zdzhXr(-&YdkQ zQ-zx-Q!gUUWW%Ag2Rdwi2=Y^a#l|?chal2>p9C(w{$=U2DuO#w#yXIaNXW6OR8p^>X&S+H0E zwmB$_!?iy5!5IA<$4Is7%ZM7sG04GKS`;n|q1cs3G0_%fLoKoSO9e@z%*G&X z4o{*wxar7Ptv9j((0c+$KlDSZ-=o#^+qZ7vPX!*?dP@Tk&xOb~r4x*m7Au&>uJ1U& zZi~k;B*136wP1p5dV?W{`vBI1ZRUVYWxg_Z>>NgRcU7{|PBtDpi7Gkj5nUQTLtpwZ z2VCesR9y?xT^VL`>&iqgKf&hq?c4ZV?YBSh?YDmqYybH5*H>$KEpYT3^xgm+dAR0I zcJybtOy_Lxk0LOJixrri-yp=*Q6F5X#SqA1m;<_{sjjGQw^+rE>_!vYIjs512$cHF zW|+upvSC;!;1t$kZrYm>m>U6F@Y~zB=+}RaR=~1r5&T=&EDr04Nj0)1wM-o{4bhuxufo?Y52-Kdq%*Jh4 zN)`ya%h+Tjz)hlF>De~3NMig}5$iL#4I6nx9(&-oTy5O6CC7yv*})_oQz4&p78s-* zpgUo6miMnl7yr$jyLXPx&o6%GwU6ETXEZnICg5m;HvzYw|NLtGBR_IgSpgUz`Bj#+86XLtYZw-h3K)^ zp(x-rho+~r<4JH?xulW!=u3;07PR@;t~{_syU9i!^+t{c3kZBLMTpBT2q3D6 zDMucY9xi?qSmIs)saNcQ%i1T!J~0O_`#nQPi+=s!-N$s3fAJ5{DO>Zr0pM1EZV`_E z;=g_Cx9N8S{>k0D^ix6G?Pc!-L|a#8V_pnMXjrr+LvT6T5kQCetQD)+*%lmlDPTu1 zo5@>?o5DC)g)TN4qg)5i=vMU=C}uL;xZm$1Kx_N*-o1OP%gf7u^5Kua`rBCRSo0Vc z-T)xbrvWUlPmh0zej*T`1=`$%d*>rgXvj;+VqDxHa3_GzC-F!a;*L7Y!4SlR+qpvv z11#TMe+{>g_6Bw{0(*}DE%>A3qwUej$;mH)GlYV>`Px-o&CxE^WQo> zJN?v?C+Bo$uv*(>Ma#G3`mVUhVsi`$*gIHfSr@3u?9ewE)P|z$hY##Z&fAm4cwQiN zGoi|@7qnsV1|y$rm|Sq^Bv)iO=rE$igJrxcqPjZR=d$jIGZ=;|rk0Tmb%0l7$pk$<5YDQtSouI`z+ zcYL#(y?r%Z=jS`8PF2^buIcXS8^|@Y0Nv(i0n=L@QVj5b?);im=ztOCq|yVZN(H2%{bxLtc-M)&oH>t&B#0rpgna+~@A zIqwo%O#s|QxE9bWFZ7?jetq&5JsC*p;MKVjuUF~uO_W6=qReOld3(gT#sbmf1T|M=S8Te}yBPb0S}v>=~@34oRc z$cy~`i@p7#$iG0gSh2dcb_N_!9dKk5-8I7ip*EaN`3P>O>ir% z{M6ZL52zlXgs(R?)_P^p{{rRqKB;i=FBqD^1Ym7vk3ClBXE*v^-n%hnWvqWBS!F|1CLF&E5;jy*^6BlhW#m{BvuPGt?U*d zrKgVJVv%Z2F}GO{;OxJ-v5{Z9zW@66jp3Jxrw0JTvp>3|F#%wP_|S*49KQ1I-2f)cF^Jm8x_446Wy)>Q0j_|=CYxk zsv}gepM=Q!?^Jpis3o9FVZ&N}`Z?ri*)Mye!x8=8zv8pRqHEPb4EvyU0-zZK-~01R zA6ftIteQQ#wO!yp2!^wWK59$TCMGtswkRZNoI50VVj?h!ANmj*DZ{3QgRBh`+Zf*7 zKTj)J+U2iH*Wt4LsC5}FsXJg z0Dv(G;H$t#?<>D_j9v@?}l<8hR9LM2U?i~n8uDaPIx;R zG$sTx*1o_9kFPzBh9CB3c8kyo?#MPf0f=5tyV53+2}W#mDHxU!unVj0PU+jb2k4%E zwY#%j{OJ1rH_rU*#+N8pvGjKr@Ke3hIRTJCjjsY2y#2g-WIUe!aBZzmT=%m9Z3C9& zR%vLToRT2UmI%ne%SRfZdBH#jId)tM4;gKuGn@z@r*sno2ec^V3m6A;g!7gp7KAy! zLE@9gMeUQ7D`DU#U=(Nxas@7ysO|Vr8C%Eqbn|+^^E!jUpf?(ie%R~hk5J+xSPZFn zSOtxkHv!mHK$d^>*Vf;vX4NC*fPPSzm{qtsJu-+!a)~@raS%q=oCKhRjz6@M11K^m zIPoD6oF%Nt*6V$<4Wo@;O9qA5T33NCW*n9Y)5j90#tKf30`pcF>)0+@v4}6h#--?Q zxl$U^75E~UF8kX^m-q77aIlt7#?>Qd?^=JWe#Zacr>h(0P5>~*U4g4t>*6=)uE1zC zeR5~FEa;Iz`q?lIYdT2!IB8lc)-tA*z>ra*k`QtQa|zFhZyjq)EZ0l_zz=LmU~HrK zt=zh|g^JCXhH52~BnKd#3{qc-G@fm+vzx>$rO4xQo?hd-uw6RsV^Mn5{uZtGcEr&g zy6M?7o5lXY!IO7$^XyJRZ~FZk z`riL3eD9|NJxks5Pql5K6@W3MtfJ?L@s!{oD?hWpKmF#mxhOa_BTELFFJUJKi%Z&} zln)5dvY9LUUdA|J=!07j&d|yr7qRg{A$BFdtq3+e^+{X7V~E`nJGmx%OKbOJp<$v!=^vs3Kt9e#87;`(Q5%1x?v z;p9h%OH2SXaO~xGdVSdbcslyzWHkLYy*#9#CxxqEP+5ix+R5_Q^QL!%2M3xzm}hJf zv)({??=V*8d7PzTXM8}-wh7*vCKH}*oUuhZp;(5cw9Ql)>kKc6uU%<6v{GCrNW3b% zBH6Ul-hxw@luY>!$hp|=I0Ij&%)&i@EB;%Xo5g4}{`SGR`XuG%e)c$xd$~onpr@rK z0Qh|333F@k@&jAn&-3Er^w81U>zix*q#(wEj0Y(>4q{xQK)^g}7%yjXBQZWqLSxzL zKVENSm?(I%u>Me|eBwHUH+nNDLG$JaV-J}UIaHjXeD-S$o&XwM05QJ7qF zO1A2dPNI%g$9yySv&rAD>gw%jZ}#yEzp(Xv+^(nO@4V%gYQvF_g}ujEU48lRmxo31 zkG;JA{=K~kT@&aPGK}R~04j#e@G2ch=&%+fJlJJ6*AOBcO%9k=GOj(~4M=SKWQ0|P ziJ++?zbwQ=WHCet4FWWOSQz503HxBR!(~9Bac`XO1$2(9UEgb-{wbSZWS8K{!Kdb&+6&h2h-8-oV&R5 zhUDT=+OYHrKq{d`=idulzJK_}_^|&mdRp*}?H$}5m`&wiq7)i-I%!z0l#HbvFic0b zu#dL@XBiaOM5hB-B>9cCpN5=2Aq;@`B8mr?t6O1XQe(tVj0yM{M7)Tk4Db=th3V2o zZd{#<%41xLPt~82rEG5}8J+p3c+h87Rc}lVW*<9l@}uvL-On=n{L9PrU2DbcxwS!l z>D?chl>i?c$|XrBS2Cf_FIFLX*g4t}>@D2c=u?f5r1Uy-1{9u4+NJr#*-~Szk+eY9 zrD+5!U&&z7)S2eE80=R7=4t2T>`3#}DIIzXR~WkjQ8rDCip5s!+ULMN1}Ab@V=sGY zE3Z66XiGM+b`UAjA!CdvQdrTol|Gg>#LX|qZTCCRZWo8+=?nG#=u=xizx6%Iz2iiG z=i-$LWywSFuP zcx>86cvFVS4;jF~W8+d7wPY1hY5=+9upq%Ye~QpLh?Cn2zyEW63sstoUv1&+|VU(HqDn z({V-53iNSG*C%tPS!P1eIt>iu(eVm`w~UralA@I&=h8AxMueeFZIbYivE1p2H$ajn zuuf+JHCTK0g)gPoG|+mbV0i3p{V0_!_ayOF)Qr&@ir$@t6E?aIrmc}FY#SBG`}8p> zHAoi^Gt^JohV_vkV%bdEYkc^93hT8^s?ARmdGkqdv2Tr@y{9MqD37L3Zr?TdJfhUO zcHF3sy#kN`a0QYA>peyj!KGjAf1dsY(Zh5_;GOL)+##r^^sF-frXVn#X!%@EcXxOCW!ZmcJefR< zw`or}`w2v_^V8a%hM2>>oBu=)uK4v!80=10osXVv&pHJg3?41Fc&=lnhaemoK5 zDfkeBq%w)+&pHkRed5D)T3?3ti7$x`IX3vh4kgnlEny0>4ft3uypInqWs7Y^o-8E* zQ7*OoPb3xji(QKAaNXslLbclo!j~G`Ml)I|6&b6%<5SA@0AA*ScW>NDOa7{we0_3Q zUD|oy@Ok7E5+6C{z$+l)zD_5yK}al+W0Ur}x{cWA7@!11#`X+J30ttCw~+I(gz}q> zGbjBzJF~l!PbQOV^pm^Chv$ngB2Jx4tB!l6D*zoF%~Ta08^)65(!ISe(mx%4pqgf1 zp?3=8!(oZnjnmVEH9fZIH_*%rK_w0#27Tj06Cod5ig0c}N9ofe z#q$`=WRH_2zQ_E-ck03JLG_1p{{IttZs6|Gp`8fWGfxEZd9k(5paB8XHQ>|9k4j{~ zh%3*hZM@!uXm~&H(|Q$~ZE3SU?N9I&0|6bD18(wGOofd@**IN`%;-&9nYNWlo!TBW zwzSUnmw~vw^oIzvt&^pMq-+t3S)+{WY5-1NfSl*Yb%(?CKK@HxNq(jU&uR}~Ycw{PQp!=(N=o{;wwi;JD> zwEnG7Y>qntupjHp`?P(QkaP787{;pOHPM>AgDau*n-VdZ!p0cQxB1bSlb~(V;{sl> zwYkM>-SK$*I=xxvFE_IC>E4Cj4a7k>cirqH;zAwE_UUTk!gm|($Cq7 zDAqH8y!mrz$#%l=6R4l{{c37%2wO8_*H^{nH-M*!LM3h#iJf|yxeN){#)|Ptq(gWP zp3-ym*D*e=pho{VJ`~z4qX)#W_5sCA0ufeL+Q!oAJ`BLubu?|&HDaKB$#jWFCuMOv zeQR+`hHb<(cXWN;jeZ0z2$d^xV@R$drqgo7p2Lg)j=Z^F$Rn3jJSQAI(AKbW5b<#BR)9_pj_VezWUjBk&A#Hwny5r7W^Cpw42T+9=S@jBeja?%!y5G$ z*8wlG)liD>iLwls?&+)_#yh|}%?Zt2_4p-%eR9uG?@cwsuIN$VMzQ^zibtr)w!?*P zU{ln+r)t}PWjVSD+4fGp%hwuS_3k45O-Q`;iabr%_lzw3f}io52{|~kQi8o1a#`9< z;Gg~!^6#{vWbSS>&+5qEaD5Q}sS#0^_4in+G@EKgiP2Ynl5{AiI)S_c*JR+VByo4* ze2UEKiMkD6_myC}f6^z2_V1EM;7;!+aO?A~AgrWMX*V7O6++KtUWod5W5VSljO{Sv zR_!Mu44I|SP6+LbXmU#;?MKzF`j^RqW=kP;CK*{sk*in{s7U0hw$YQO*BM7BVK zse(@T6wXU-%ERh!?k*84TG2ZT0wdBNwu2vZcax^|s@L^y-)Gks{2*0^6xntR{)nNo ztc2ACFVxTdoWna*pxIDkzSo*xhu}tig&XIq^jGyGX97`ZNW*e59C_J^yB3kTyYO6g)Z=aNuoUditNB|fEbiXDGwWJY@515_;uahWiFJ2U6RT^S7_|-I_VUzST7S*@ z)3OUytD`2mCpvA9?C}1e>&ET`LEmbI)=e~7P6RE~zuUPj2+Ix)H^fy?%R=~w%WFMv zvM_pS?%7|AFopK{u_*)$&s6@_<`?a5%Py>lZXwOLd>XwkvN^b`rj?siQTlmgIR~v)Rkw*CYm)- zpsBz^*zpkX_|tf;^QunSD)N)9kQe}f#ipdMri=~vuzL3pg~oZfTm1kqNFWKfZ;ZL8FcdSC;ONjTcu**!S^cV%<$DEwF$ zdOQfKZw8J1UESP2f*wN=$Nhb+|Cfd$5GR11fxf|s`G4DweAopPAPfK>Y=bw}R+r{y zr)CzX0O0-o$2a-6viNuE&*;?L4ET}!UwUkKcyD(SlU=$-}rZYHXpuBwz9muvby%~-`dLIE&Oz2Jz|Y$ z{l9yQ2gu`NFgRj^5WM*K$ln9_-0DUYn0Rh>W_Eh#{`%$yvauDuNiaP*J~1`-54^P# zwMjVndt`Kc`tQQ>KXCY_FnDu(qHpl`)ZfL`^$qYQ7_#+eVsvoqe`zoT0tSpskB?8! zEv>GD!8-?Fz*E5K_1Wpk3G{G(XLEZW41ix=K#z~1N08mkwdKvdZP>+O#F645WC#3j zadBts6nY3bJUTqs-`Uz&TLbT$o*W$>9m8O!Cy?!}?VY{H&N1}-KTV(PLm&r-k5&OY zy}Uk*f?^z>grDP`!7oua*Kk<)DFzG{ae;k)^{DsgvxqC~iyI{TEczU99e#s%b$yRQ z-CdqXAP8@ex3|cv%Zo>|iA3NdZ&7!*H`kXJkqDx5#4Q?ie}}w&x8h54Wn1~4 z-WQinAK%FM{7ZP&SDx(FiR9nO8Eeln&Ad9U11&_;t4`}qt4@zbuO}tE_t%1Puh;53 zkFM|D`(8@Sfn}%i&JNC7TS@sd#~Y~%`zTI^17s~62|yQzx|S=*vL(=YE@|&!@jvcS zcQbwnIKfq_c_At4AIH@7cvu*;vD_iiIr=FkrLln= z0xBtTE}u@@MY5g<>)>v!Ya}z@?!Au27z=$MS3i$_tE;7fso8XRoVatgODg6p&7xiK zdFOKYaLfoYKATzAv_iCVxI5mo-s9j7?mk0Z3Ib1lE`_0k-9F*)vzqFtrl!F0s9rQ6 zcV>-ss|d_)rG2e#5TypUU1Z1Np=)rphL!1^C-QfU(=4(#u4M9J7D?c+5OH+tp$lqp z#pl+Qd$b&knR{F9@8^HTy)!1iR;-#nX4h=Ye?09a97%*ZFSdh}{gPsk4{cwHUkH(K z$1RgHj>w5?#5=J-yzY0ATM2)`K?p&p^*LruXh>oRw=R5gAX? zP3?TLLdBQwsQAoMyqUB1Ff$)oN!v+SVct}dQqyY$PJR$>)?lwIW(6cOS%29>L1}6F z^$E*y_w~WlnM_KmE{n9JwQHf`1zz7 zKVV2^S?JokwDlGU6|@h(y9ZFV8Ynsb48(iV3aCmSHW0N``9VWs@a)vn%KtZHlc_<* z#Zh(cz@KMArDu!it@DMDdqB4n{W5&|HhtBMr6s0{>h~X?uT1ZmF@=Qf^_%zbb#-JB z7Z=}Lg2nszir+GtvA((Do;!T-k?vq+;Ya`Wafj(9jog*)tsEX4>|Qu}x!$5y<^;p+ z!$NLly6%EOa?&iJx0T!vxsa{m)1;+_5HBf4Il_9|Cl!P~8yjn5J%(AXudfSFZ|dsksBxEbw;u{_W%qWx z1<19(X$`(@bv<6)uHE}l983J=tuNuL@_sfi9c)s*OQqV^zGVh>4Iw1NuLu(omCp~t zPt&H^KiC^NO<0SOw0;S%Bn)U7!IZCCOJcTI_=}%>t6cx3^yi#FU39DF-@Gz>Mys+I zrd7ODUk^R2TtKSt{>jXq4F*|u)7g}ZgV_2%=H$(V>EW@lgWZFJeHY+0xR0?MBvqgT z^bV4K0FA-|Ha}QURcNc}$_g`t9;js2lfp;DR_~_@RnNLeu5NZxbrmj($+mP(EP?2G z1?2ozSMQEFOvZcyy?$tS-lL|bhF;wI&bL&Set2+m6W|l#CHt6v{W%#%%+24c-{3pF z+0wFlFxL17tJJW2-}JCw=c`c|yZ#@gmUxxih)b!8Uil~aNiy04;Z{dd`i786)@Rb) zCJl>|VBHR=C{TBv;p@A_v69@E;64ffB%zj$s!akS2}!JXpxc$Cd+Cw>+$+XJbk6+ zfe}7^aX1)uF%dQ+zcge5+j*Hk$jWh z_d672Qr)=p+qt^~#U9;z$5bNozqvTu)%50MEbulN(t8d?MSULUF3d|V2=WnrXn!b% z>ZEri_TOr*4O6-0Gvo!|^izefZaf-qrEaJb`O_)~zI|&}3!P7bepmU5V$ksSCSc*YvDsv=+A8WL#o#@l zR#u9P=$rS(_Gti2RP+~`SctAJ>vJOLUF9N{Y~g?TaG2~}xMggG&z|`VWXLKAIOyqOD|eQSN~I zLVF3&jq{Get(vYL6Hodq6}$+w1+Q|o{TqzA82J2jzpigA^1d(g;9g5zO^~I>)l>Y| z`eq@6HGg_~BRC*H=n>pmTz+me5o?H@rSMQ<{j@d)!h&I3&A>*yK7E|W@$AM|OJ&{Z zJjA^j5wQ&=uZc8dr{?6WxIZBoO}}6p=eP84?huZ1vxi%xHoajc8D<-`e$QN|;TP39 z^JiY$>qvMTU+%l2js#?+l1fV^ChtjOlIa}71YLs7*MJq=dZJAjJ%7gB{r&x!58A^g z$OljUt=v8HG7?C$@%5f{)ID?oMl~i^*@dbByu?m%a{hoS5IXeNJ^c9asIv}4$`UhB z!A$P~JH!SJj>^0yJ6)4vSnu^_ECd&B#bXybx*k0S@#_w}rqyB!nsr_{Pkq5mztm#S z*DOvhXC#cpDlR@y#TCH*a$m2Wj(r_4>@ zxvP4grtSr2Rx(q(=ZA)|<_>C%;RVk2=WpjZZ-$>EqKR2@9pH?H@NpbGxOji93|yoi z{WxNx&X2E}&=VkDs|}U_!VQ+2_S<~^+?)aN-T2jZl4q`UF{3QOF6WJ@p~P1{cS-1x zOtb#}7|t?)~&pB>Tj&L)v81|8X5( z$TCR>3V{SXfX#!aok5W_xklLXSB-l;A?!1Ly{==or0-R`&n$tbw{5UD{tr7y8zgJL zmIfI!EA!wus%?@Zn%tv(XcuoXWb%zs2ssbN(MDBe?Q z#CZKu=cT<#sH(kvQV-vVkhP<*II#tz@f%Xox};Hfc5PE>DP+s@p0&@P2Gw>43t9*D zcWjK)VY))r%{^7KK93-8uW#xV{4Y+L4i5we=wPUut^WQNMhW*69UYzho3DViUofb9 z>nHy+Ip1{yz9VS=@#+zLBshlSdPeAyvwP^vqF(gxq5_WZ%`BteDS4N#D8osX*@|`+ ze1-qNw~pR*NeEnj#j!eJDf9b9uSDzrr6iEQaW<%aBvtga{I{v+y2E+m^Ztcep%@Ng*xEs$E;-FC5Abs}BJ4g}_ZubHD~ zbC9+n>y-jiT5QiyIarHqPJcRFe2JWT-ydv_;N<5h@Ay6`=J*;LhmrkwW?p(?{Go)l zyn25=Pp?N=Yta9M+0j_LRdV8;ftU;)mD3bY#HE`?w|pcWj>l8)02-@S{}Y;`uuH>$vpweJxykZd_xr8d*pc9-LUVzAg~n;|u(+LiRAtZG{2m9t4gZpj z&t$r5ulPbp-764|COf~PtN03ze0T41uyu(=r25UH$BM={%%7P5XC+rH{_A}uB$O>t zhTk;$RqZqJm~}L0h{H zRtR)$&-ywDTM%Ixe8?!cRgOGMFTJ@Y|0+tr9N*+^052E-N)xT+M{CQ5XEDtO_asj` zuXSwjxU2PsZTM2_mGi&6kY)ICmQ~3fWsgIjzJjY!T=bg7|6Kz`B*}CzB38a3sLA`B zhJyt+`9wwTP7XI|>G$RHWF?I!*J#mLOMfwBc{nUft7 zkzRq2z09fo^3f&xrge}QEA!)&-2rqfI?k=&O><4Urs5&^$*9-(_boD@aX*StVQLJt z2E5olOihA}gXI6M1m0-}q_XyA&^`QMc^K2Y|M{@M@b`qX+p3niZmp$_fRftF$6_ba zaFrUTrd_|_GHz9Nno-IG?oceUiBR{SIWxqLi($cyS^BY0*O2Ex%Qt}gB7Qgb6SYQc z!+`6fszv6u6rkTJIcM?Mmiy zd3e}+$hDLVyY>=63tnGeJ&1*|DumdwhP9Ik63M?5 zA?@I!uFFExu>CbpNYhDljTDpT=fF>c-7m#&lNw8`#CA7}4i$0uAuFTihXf{n>#c30 zS;0SZK_}WvECxp3UhA1fPt{|wPeF`%R%pLb7fAm4JjjCSFy)2ePNB;!)5Y<)eH7Sf zmIMrg<<}$4InU&{_`D)8Px4o`HTp|7Wha^*90~&M(Y{L?@TPJHMBz%dPJJI93T}{p zgCl=5E_u)JRt~4F-4tTBx>;+0O=AS|jj?!c@(GWqNI4G5dHKYGBRl$CyHA3w$SdB{ z3Tzs>FEs*KLg_+>6p9o&^W6B)4Wp5pW)i>;4q@`f4MXS8hguKZ=bPwrQ#h2xI72!N zmCtNd<5YrH)_I>Beo=o$+M>K{uxW~$Svu%^t-X&JuATKcx704|I*UKU1N%hNVJLR; zg_#!BGCC!;4vy9TJ`K6Nh-x}Jpn_veD_otA3#RUmBbgp{1q2Mepb}@>l^MJ3K0M6y zKR(k?(4MwU)wq=ncTdy+c&(}Ro$Ul!o9sS{j(wN3s}q~JD76x^Q!F0?OzcX2l6f2b zJ~(1%F|66`fksg}^{X3}3hKKQ_M%@s*_s^}b#(re@m!eAcBD24--$oOe2#aB=ZV3&M`NitZ z@?IFv>-7s`s_`^_tXXD@o<;yNG@Eb zv4-hQR}A2OC}H7~=v30FursJ7^2ibh2M7Kmf#0>|zdz5AS;!o2>)xjb^H&ht=-vfF z$GO4CTC(o#%KVk%@~yjDIygxf(M`|P?lsag8>%(wkW#LDMZMjp75sv^=)vDK>J++G z9#BF%lGp3k186IoZuY3$cl_KOwwr-9!L@Vy`MT)`Gz3) zOUN(FLNSDhwa5pS>g-_RU2Ik5OG)a_A20@U7uJLoGt%-!XNk>-e}pq`K}I%%vC7)4 z-KC^Do3f6NQ5RTby+@Vl<(8<&<4;~<9IG{OSMI*>--r2|nn;4SJMVw~1JYP*0f~Eu z9AJyJrrtMHJy_jqsoX<*t}mE*T3hLBGIn%#7wqg<-fQ`4vLq9}wG_0ToTVU?#VC*H zON>zB?9h)h_=7FtRy`g$@#F8`QY>K?{X%YxxUFte;gRDf?UbZ-m^zOd!4Ynt*MZ~U zzFf#&4?kggsw0*(eqlo) z@GliV^GNTo7Id0PNBy!vTP~Lhi$7=Ys^m?lK@cDPY zsanlSolq(4cf?=ytpp?G9v8wO^!J9l3E<_Gk^4QjQ7<%kdY0uodF#Ah!CnS6iT1Gc zq}D?l$m)*Tz z>9CDtzitt-8@naM$gE6tV3h+)n94Tj1qFUY0@3=VDWhIJM@3sDTW=Y)*yo>r^~+6S z!{Z~=G43cgXk)Lz3i*YT3T^a)=(ST*%X{a287z4p;V>;W1sUN51fyb<=Z*xh4S6um z4SX^c3-S!|YxRa4h=)jvhK_;$MbGakfW|BiCcax34!kg^uR8%qg)pmDms`b@EwM(F z7T0q-1awQt>Q8140P3Zi+^QKpONs>t7)kp)oI-KQ*kTZr@xt)^R1MQ}an}c(iV}(W zrm&t&O|Rka&F>}kBMTD0=o1o&G(G(>{!abPMlQiK&mZFgY&M%8$HLo+Aa{6pb9*j9 zg#R8a^LtMELGi-9{uF-yQcUmraU9vgvfR&(-BBa`V@A5bPpvX{!?|s4Qz@HK^k@62 zTJJ2lHiBeZF6I(T1y0(r<|R((ZC3(@4JB$%5~Pe<0;WH2rA2mli~gY?d96LHq~uBiW;h zXVqRrWtvO}&;-8K*pH1)mLlLrcDHtB>~%&jbQAZzkyF63(wI#%dhSXv5AfbQd&KZ#l4-6K2!!Rk->mPYHgp@WVQkdYLTm%fmEG^Q*B)6mcm9D8*)W>))S_{lFM4V)C^RgJ8Yr zSqIa*rAD*z_tCa981$sbPhl~`Ui2)nhsp5PR148|1I@)j4VBfEXpuhszp~0e9-ikm z-xOyk+)*FVUvWE1VKhFo5r?}C!3=9(5MT`u*2!i{nX%GfLlM?g)>n?ai(US zfIiqcHCZ%*2OjOMw31D#q@&`XDJ5);>qsN zr|zbBW0##5cOl1bRrB<5NSmCDR8NXAOhmH+Ecn_bg>3yX9mEVh@2a3rNqPC|;OPxM z>MwZuV|%{L&h=OzWbzH#q9=f}2O5ZF+m{R)ejSW~gfhQ90mb!L{3i z{;huQdP@)~xUr#6p`Fd*rz4-_hdPp}vTshNP~L?#1^Y!ODudH+rI>Zm)$4$;pcL^q zaYbb}0N;(6yA^h~vYn8vDtR@d6h%dQBsGz=sAVCRkmk%*M(`Cle9kEysq`Z%W%oR1 zM!*^Q(6a08C99H?OCX%?9mx!^;BLo!(~=WG(({fAyIfG9{f{A5ztS#0uPg;^)P>KP zwL9mt!Hw2%kdn+wEa;g>NC>3-3j8nw9!LZPq%9{XyvVDU&HfX_!GO$xj=7X{lxuU;2}te6Drd<V93T7eg0aqt`%9T^P4O_p@YY{4@Y45 zfL>^KOAQQilE<@>l;eJ!{z97l@PqOVWe6lYEjZ@v%jQec7XOBP zu0|E`cez=l*#PnEKYf>yXO97c5AHg;41~D_1ikd%yd_kag$9h4#y2_i z>z?8^bx!n4J!xAWCMRnT34IftEt}k86nrI!P${tyFlruD7pSkP;RrICfJ73f@7pQa zpF36P23(+?(tdfOIFd}<9T+O(gEmsftR~T4sIt-Y99RiY?(&!s+JOAPvykW70mR>;{c)qyeN<}i&C`%t!H{C* zyq^|U#v@r>4`+e=9;({+; z#<-206BOR_Y1`PD794{jqB>Ez-ylXFG1XSGg)F~l-u#9}df<1Hw;1*fQ82WHj_FpV z@B7eG+jH#^s4-rka9uW~`3BE&a5FHT*{Db}d(X0281#(o4 zl5Qyj1To9FJt!I+1TiNpDk7JTsaYFLy#)kybJ3TA8>qk=!qR6pQ`AW2hki*ZB_|!r3Q5bssQe;DT7373K(%j65J&GnT z=1YfCQAGsdqhF}5-_t#M#?`(AaB=4t^(}mRKq@-W0U)L)Ek1y6As2(mI|F#9g>PL} zD(%XotW=(az%FBI-@Nc~7y9w?MVJ&AlU_KsT#b-8L%1HCMORozSo#% zCTGySpQy&p=Np@T*|Bayr?n>`TsP<%Lvs3?K&5Adnoi7Sya&aq$NA?`AHNGnv1`K# z9!-hX>YHrzuWpeMBB6Y-LLw33J$yAru{GR0$n)hiOM;*hQ3f{Qk3T(m>ey_RoIded z`sPrHJK6~ue+Mm3VnQ4 z=H^{#X&b!WY4bfEfX5ih;>Al737}o)J44ekts^IO&q%F$(E(Kz%_y9}Fxo-FRG|WA zYG-LsQ6J!N8I>37FNEkDRu zq7~*Wvcs1)mw!6|;4qHdN%ZGj*rQE8{CP}0vWHlTgnhK>6&u#p_~=dmVpkEN$_vV_ABS06L}SPw&T7op811zjqk0!hQ56Iyokqa`fp4j;WBc7z;K= zk6SERgE3%axQId5TPnGR8h0Rg3;FF!_)lbxaQ!4Rj_kmXMuUVOn9=QL!fdNGS-LyK zUG%N}LJH-@UNe@Uzow zf&dayJs88qJ@{4FS(9Mz>E^?rrR9{Gd#5*K3R4{Dd8Q}83GUl+X5lgllpi$eu^3K~ zJH&^X-=zgIH1Fn!V2a92J{up$?t9_P^AXEKRrO=w3E^`BZqjt4|8z9?b_pqax8yuh zWmcRWbZ^R)-JYL0ukI}@A{wYWbJdN><?&rACg9QBwt6R@V|Tho-s_jEh0Bh48(F_z1`r4olJo$7N>VRiG_cv7KGU^}+E@wM+z z7`=na@d9N@rLm8y1mu_8U{)lB+Y_>Gbt6Vhn(HBsWk!FlIf;`~Fgms1j=NA=?A(oP z!BORANL4o4>W6im)oJj-ci`Qo6)f1YmlVkU{MH=pRcdFzae@(By;oV-t@1s=_v-#h zRZPN^Q*BLLp{`_J>HHm{u9O`(>WlB~GGoE_<8lRL@v1GbovXk2H{aDS`sk-Bq*ccI zHX-(u87T#&HcuVIn$3JvF{q8!ci+JkmE;hE)^?@#nT%&^vu2WjLa!hCdE~E!zpe3a zT!rIMxopEv2+eQ?IpvgYtTsPw#8Kd4!Qyn?!JT2^{}gUE@WAJ{pp%08df!xwT*=V1 ztv!f)$i=1=n#1xg2U)^;1|e6moeOgri6YWB{-#NROm9}^kE!H3G3KSk z#JSKSnDB<(0))^EsAs4sw&yPjs)JkG_tMPJ4i%5dl;@%2zvL*5eFiv(e^i^o8 zbo13Pa>cm~(BMX31;DOUIc;L$Nr8TMP|IA{F>A=~f#@`FK7_?88BEcTK@>j93S0|0 zfZaANf&ATDuOFdnEFTLy=sI3`&l`SY{HZT3bqCFrQ7Jc*5?Ulj?>1yUX#26{6*kLf zAK&Yisy94+N1$etA7fpY?drp0sM^_A^hqO}S-&g`?ON+lKb|CSbTk2(NHDH!L9yC% z?Dppyrr_h$w|{Mt@8={HbVcxbevuz~oxgEHBK(4v^=fkXbmIa0f$LOs)IkCayv%oh zNch5fQ;(P6u=nfhDaVZsRvt|+4AdPYbl24t9NdmfB)k7uHu*K)PZ&+k<>bFKngjLC zJSy#8_s)FRCg4X=PPgE)l)dN-l{Vvv(~i!@i6pp`slUYT%t+COIt~APb8*n&TLUBx z|MuFV?8j5?bbYTZAyKRaX_9jC>wuWXz?uF${G^yg_Wa+I3jd76^x7`D3|f8x)B-s$ z=#T30$qRnQRfrD=h|hkpqEw64+vb0Um)PR|qrVO8q57h&H7BX3ycIpZ0#N!haFS|sw&kpu%~Rh z`mbo{_6eDCvn|=u)L5)VKa)*>dmqkMrna`o)^B?tVWiiKvpeHT*T>BQA6_Hk!W_;P zo7x_h?cA@RaA=qeygK=XQe?RU(gl31+;(;mF+VYjQz@aR|U=M@;YAsfL-ntqIa6!JJwU(4Dml0V?2 zB;9t>kc@j`JahYQXSQ$6X&*zV85)Q0!gEAu6UN&=tiCBGdx+`2jqmfTDvdY%S9*!; zpR%zm*4J$GWgj?*y{IyQDxn}5mas==JHgK}|{jbS% z=*r8%O?OkV4>%_+UuWFKelKA6z(66^fxhz0_mm-BTPz`y^~kS4UOrK6I@((HKZ>KL zt9vHiAFT^cJG`7SA0~bJHZG@a*N>#`@?*_!4eR?WUJi8HWf->}jw#Mnm=-#vd!F&v zn97>wR!!$kyvyy|K9Qm)ctxVy*_SJ+97H^;3lHMn$Q(#r7-d4TCX$u#OYB(-eECaU z^0=qmB8}WuO>9CiKqJp{mfBM|E}WA85vg<@$G6`#BKXj&1Fyaw{rCJa-T1N!D^#J6 zjSldZy!Qgs?&$^HS*l~|z1y$NWd>jCmdhuVw@Qi- zV0UnDa*lD{(qqsn=TmsX$@X!XX`SBqKW$5c_Ue#a7d4pKY(5iRQQIQnT1@8pFw(e2 zkAS!C#!U2<#FxHc2@#WqXh~?G#}3(UB{k=m8B%`DP@t9gWH>5;sE^%4c^>i`?l~yh z_C|KUTuL*9Q8e$Bf>%TFWIvO!l~@w<{*Ij=*C{&M>OS7I1C5`*h1VKgkv=PAmy9X& zrmW9MK638EmQ;58G-TT;8J`j*q<*O~;T+s638n3djUAs$FIDh_Pf5tW4zAzyVXE<= z*|NL^?q}bRx!h_5$YyWdJ-m%7{309tArsy#J}}gVSdIzqWmu;5(RHXGbE4CLGA)_){*gS*2Aq z(ITw8f9t8FeHKU=>mKZzjCfbC zCnk+xF77jMHax3px-zyY?M zA>moq2(3}v>OArI1RyC-i2B*+Hl`aMi=a&wyI0?g?M_xWUcqQ^ro;d>eWoGS)%xSw z#(c`pFO_z@6c`Nq;*KUi~Po!X9K;wn}?Cac0 zMncu>6X5D@x4oDge2jUux-xa_(YTDn4*DdRG1RJ8-?^1A7`B9_bG!fj@UT&D0kZCe zT80>TT1bk{wg!n-_)UhGH~MthPcXT4le`q0uuuQU+p)SNmXlaQ!~SQ`ULH$Cly-WG zV~aC|?L9ulZ`oYez9-%Nd;Puk7!+8V)v{b_jLDjbc`gUu2)tlgviMmq3`uMjwP?xf z64x`Nx9;PR}@_^6H;P6E?zNIBN5#v7Rl7L`gL2-|Ud~N%A5wo> z;#(R{Z*}%sJh}?pGjBZC&N%UEssCv0R=v1Bl&bicj>yb67|>u+DkE1;1JYM+b{GB6 zUnV8?Vgas-1AUPw-R+6~ zH`S9&{GVS)4(YFxBhsnr9OdwLg*S@Sz210prELN{%qh0~7Gr+Dp`?7zca|mS{kT@F zZM6(!(AShywUTIF)g%6}u}321ze6gV@!K0IYt0%1qG^z@;LiXy$gqO^FHYTiUH|%^ zOfkadXbHx#P@CZM)pHu)7JL)F^-rzs4EoTmV^64~*V>y`_H4Dzs4fxWw6t3jCceL| zaR2VqkG9Sn<=r-od^#}=E;l(9A>i`NLw^hYKA*S?aSCcr7_u_!?P))M`wtpCxTF)) z;jX-FW)?`#@SSy3sX!r9{T-9u=z5lhK%+LF=Ob>oi9Cl;y3Jl^1?JahA$O=!jA23hHLVFqe+}B>b6krtvt90L-LwZpRH?1I$7h->M&qu`d_=`wl z#*Ytgx-4uX(kjhGO$}d$OC6LGvS5cJK))5D#J0>{txlI8OXa9@rg&$sbZ~w&n7*oh zN57M4CA*k6`bjI~xwUOle~q{|TsfK92A460oF*zRdj%z@39(6OIOK>y2YB^c1qeM> znMM14qI{p!#V9&9oa&Vv@WY14TsSUHbE2zTuj3$|^E*4LHvDX?Hg|kD3K88p_kbN)%E-!wz;`cRp%|25BS$SH+phJ``6t-osVL@I zxnetZB21vd&-eZ|MvxLWEvnG+7Ce7RRPWAmd1tTX7GrIXxTxyGc!ihtcHCw0efY+L zH$ts<79L|_(=~V4R~*K!*b&t!m-$Ai|2aRcZP{lbJW-q;&$kNYjGw8qO5~*ELEBG_*V=Y>YRqhPP#oI za*dlam!9`wt0yf8Gl<=Ft_{d0cA}-qFr$#9VkvyMg;w9D=(-+2K?lIeBNqrUEvh$l zj8{t2Tx&_jtE7l033W@cK1-84$a1I_*;GxbfQL?h%X+5eR>8Ns_bleZ92!-Ai~8BuHgyx?Ls7z*V~@g*)M{ZKyuL+vyHEo&u2;$yrax&#HKwJh^ab33c9eiE)Vnds)dWz6}` z)ixXhzQ-a6fAP297&+Vf=Wp+wqj1C|Y77ltSZFpAE*)}FPV`Uc)9e{*yowLZl<4mM z;Jq_~{@afCRjJ`d{cM{;9WDr*HRN=NG>+>MPB(j4me|Lc;fcq$65~0WU#4%}9^TmK zUl=<)FKt&Fbilaia`;9NtF3r;jP`jroNH?G4!WurXUjI6`w=Ag_N#zb^FrK@R%s5T zD`Ts!f7YMaiM9m8j9ijsq}tYz6dT+o?W&2AcAAyfSg;@vM{)k7rF-jegPdK4oB^Mj zez<=-Vmxo+A~3t6H(Q8OG#(aQHWbn?K%+atuN=P%@I&&K)owjdBR5iwinMkD^?cK%ermNK_c&>I1keAfv4V}skg588dmd7Woeql64 zCVX;tpod|o`;*jiP^h1HtONSy2)I0W{xmcvKQ|=Tu$Q*(U)k`Jk6m?3hDK83hN@5l z*4AToL~Y4;52{#s7`~yLpzH=?r$7)<+Ad~MI0*sZTYr3~+z6vmBtOM3Eb}P*0BTMJ zYT1Jkx+o>ZV1=#xezMSOk9ZXOfIeOU zEXqGHN|Yos)a%s<+cQ)p;IW9)&$(GP7`*Y%5{(Dw<36=-O6eDK3&iP@y^B?N1^-fQ zw7G(|Ndb%R@?GFQzb!4yxW15BZ9Z>-Pq>=o>9vjr85@|I@9}5q9Vm7zK$c6wKD*ur z&bpguLxm>RmW-|D_zkfyUx0@AhC@2E*`i3H-?r%eeU?#4WU zQH8Mkw!$!S>8?JxZn3>p`;@c2?@?!kJzfDftLf>B;5$oCu9EHI=9s_z+kZ^Xf6%6X z@C*->KsMQQTx0iy2)L*biRKTOdn){p6bJrd`yle?~DODWr=^kEokuhD=X~x;}Gh z@j!e0jr5#iX~JDEqwU>y0}+%yNv}AXfoaNG-Zri5_CrAlq`TrnauQk zZ&E1|L?t;RV(6Xx#&!2WcO$5 z`8{pm-u2sj^q_dpV)JbY@dsUtY6~@2E_$`l+fV(4criyiqlk)~ZtrcBgr}+8v=GET z3px{|6zJ%Dxp7&2(0xu@bz<Kk&^9 zBebN2!>mP}wm>ijz-qI9l%Q-*K_PbvE^j$iQ(3?J&!f|()>x~zV1b2PL#E!6|3o&b z*G?V}COiFSG4IYR#n}!16ZQE-G4oE9^xp!|I=7{4U=;2P5WR592q` zTpQmS>+W2to>D9PMTd(QETiCc^5;9&&DV(SJN&_KlkbnXKi%6^I>VDX^Oq_hOEf2C zg-er-0(GnqO$9oC+Sc>nPwTBNwYT7{R+py`^Qlh8{{y5zTff;~|MK27+_{0juD=I+ z_rdJ&M~v!IPpNyo>ioX%o18uQ937v-hyR9a#5$Dvo#J=PCb&vRsRtMF zu6`ahpGQu%zWq*gCZL(;JSVNB!F_9K+-g&s?A#n{S=)Hp0!lM4WNsbGrl{lDie@A<&a&dxjT+_|#=Esx8mAL@=>*U8sF zQ><$cGJn)H>bYY@)X}wKE8p?~+|!qj+Yk2hTK&e(ooMe& z4}N%We;?xZdw=8j_KuU&u}7)8M)%W6*XdEvT~5J1(a*L`HM54@PX;rwp0pL3lBs_M z#nq^r>;TKf%uv7T4B?{Hm)n?(l4W`lsMm|B$i45C7Gd zcl}GJ*U&w$u9GgorAKxE`$j)9viZ^F8g9Plml?ZtAUrblK3lznH$n-U&9!gkY9@1L4ra!!Dm_Ck#=KBT+=GnK);Ft%*fzY)k>Ww8RGJ!7opKU| zk*s$$twNd&g=GD(;g9beA1!xwcHewpzkoA_#YQeKy z+*nNbabRK@0J@`i?H-iIok%7HT@@rXCxvoHRxE%@kmgtk7*Y*ob?X6i4)*XF?B$E` z=m$*=PsmDUG=z@`;042c`OGu>HTk|J$JcCv9}T$eU;8MHyz{4|L{q|b=2BNGc&(F zQVK_3vi6)hUrE%#g_``Q1GN*B=UB%)Iu+9qJXGzZ(=mNVc>DR(QTVQew{)uo)4w?v z=iU(dP_5ZRl~1*^SlqR+0eesku~#%0#_h#ie9)1gG6=c!`S5*4?(y)YKw*P_&|Z+9j^}?8{_-Dw z79jb+0M#=5an<&&!q>U&{nq*YdxUy=kRELo6kUF$Xha{q}K5B*a9$Mm)sYb5)V^-GeLB`C{=W4f4UKe3Ebh@aSm% z*kg}PX49QVkB?7b?aXCdML8|MLg*Ft)%Id{C$;Ara{j25>R~PXr~7Pl2G`8bSFcG| z+*U8mfgiWE)(cu!{samWYQY3_(Ms6)va#+QTlq1B0`^Pg@hRNQ45?VsU}d+bQbL7ije( zd30_$UztnM_G4N@TmJS8eUNEYs1jACgZiXv=M0&1RjrPE-@PUt12MDOs06!=S1m{+ z*buT705TXq7or$k1}rc*usB_VzyZfSR8QbWIisQFvV4U&Y4dzyi)`SAx}y3MZmA{) zW~S9Bt^Oz1-_=n9~Pd6zc=-n+aZq;WyT{A3gsL+4>V@Uxsvr?_0# z7mLLsSKj@GFD~IiVEplqFU%_dPd#eCC-`)K@qxoDhxeVGoxq0%Gm3HIyKJntaUGL% zOT(lw_vZ4m#EPlclGdzHFI`*JffdiF73tg53Osv@H+8P6GnHwC1!9T7V$2NOy47W_ zD%R)qMv^prH6V{o$lie*_X7viRO>N##KFxS?7;pnI7(b4aRIVbE(KROu1gn7%4kzh zAUr-jJ6-G@Ub$~JoqPZ(Pd)mm+55v=0N{i_`1hSt_@jUD&A$r-ZNn`yH&+@>u}`Jv z#l>1>Zk%Izo_*#w3`egG>z8vFCfkNpwzcUSZo}r&Wg4GxZ@#{evFlkb@r@H|gjwwf zEQVqbJ*Y%6oT7I{&vI${EY83bHAk$;Y}$x5crqG~03*Kaok&CCY8j$0riKg`-Iz~= zRf0oAeGyPG5Q=J%9uau54g`dlhZo!eQyPFQ?&;&nWVD1mo~G=Bnc|?T?77)*Se4`J(jaG^AQX^;igwC~{7PU8a>C zz~zvOB;yxq)H0bC%s`V?Q>o50VFuQ856W=UB^I+?~0!Bk`Trjh7AajJz{?TlLaOLmXw(smh{5c$WhEr;)z3m zMehVu3>XJZC8oF{QgtY5Nze@m;643Qcyd2KIr;YQ_|b=73ykIR7aoJx1U_af9*=k4 zcW`)c_3Z2n|N38pVp7G19p#p6xR^>hS?dL8Ze>2&X)fnj6}SYncVG)om}IqEYHY#O zy3$J{kj(qmr5pk3QOR6=L^sxgPwGk>te7Yk4H+<)9)lMF!(Rx5L^TTwam6q*X4xM5 zqCG|8WL&Nbiy4@1xp1F9}^XD$K|jdU+T z13Ct|Hj8+4jP<38eUxj#BF5doRC~taWZGjicP7OqE-7XtfFz~hm`kCss56^DDGfdR zxjgLc)7dV(zrTF10mFCy;41)T!{6K4ec##K{CZ$RwqiT=LK1ITqoIgIaoZiM&&i(t zaT<2-^~foD<@=SHf&h73YNUxP-zuK0)^hVU}LXfYp6XFK*4fTunNzwHONXMf|_ zgIB!am7keRCgv{y!qNCc$w9+fLzGn3$mhli%G|ixFxGkHT*Kt6=Ll6wORrgC`i%Nl z9L{K2c=IL{YHga8<1ie9@669s@D6Izt5;J*))@1sj5BIe>x_^YKsx$OT2% z4s@L?$(MwPz!ky*6O;lb{0@7F3Q~Z2fbXFavmh66hfo_v8Ht%*9O53I#F>Mc9?!L- zR3b0L42Nmh{E0(xTTIAlA=Uq5>COT)gNPUnLmeytW}Kd%F^0q7R>Q9{4TE%8q2Tj5 z5}_zni2Itn%Ns!#E$3!uh7wP?Uf*bhID7}da({n+JfF`$zjyurd&l^@zUz+pwEu%| ze(<&M3DDP`o}R+50yg{tfGp!ek3xHfo<>-PemRgIG={FuY7eU~=Q*tQ@bhy2Hv_i~?}=apF84Os5f%4# z+~b$a(QA*s>CLZ24YS)Xmg7eb_74u`^E0e3qBW(ui$UF&jGubln~C}Q4g+Ou+cw%=0wd@ZiWr@k9$}$|rVVKsHVx&}bVH3e6*5(L41+2IN zp7S$6=T7IrRWe);N#gkIf4K{r{cJIP1Yza^;OOYi!#g`O{IUT59l>5>EygO-Ds%JY zro5y*d2+8vcm1@+UWq%CFz;}=yU)9G!S^2F+?u-1#2M|xojCmZXU|%d5}bX08^9+; zBS;CiAGo-zuTJv#1R6Qm&~lhVtxv|0V|pOmI1mm$9pg^I)?qIO>;`IdQ9Cdbi({;9 zUsf;xext362IZz980y=R$$Yy^*X4)pEdx{O1$+ zAmGvQ@xw?oUj($bzxNi|0ihWUYAGW4g&y{qYab3asV{HH24f9{lde~)b!he~WV=Y% z=IW9+fg0zP(aN|J(NmeLI|ZhSz`- ztB82I1u5|GK*Q-x8vH0FcqFnTAPhw*9-ZQR$QaS)FM}|O_!t+t^mrn}9iHM19`&|9 zA0I9o{L}w265a#Y+1q~$x+nP8f#55EA3i-hg9e7b6C86c$K5(mUfNh|rKOI=RvZsg zi=6*3nk!E1o~N&46?9Fx`bpy};RN68Xb}#WKCc3Z`35PyLKhbtzM-gm>olA~TuW&4 zL?j$WuGoRR^wj{&ppr2VZUo`S5G`ArLw1<~(Fo{M(v@#{;Og(}%%%??9UU#{OMvp} z8`C6Jb4OVtpBpvGT%JYRl2^__Ovxc{{uuCW`lu8*dEN7fG|Zr(^Z;ql>oUzC+=ZoM zSJmps_nm0sF%UELiA3H6pLrKM!9_Y|2%rqk&xdFRmjMe*7$h1PN+co&8wNgesv6R% z|26=_L~rz%R#htx9e>-27K_%gi#A-4M7?F~as`7%cuNB$CS`+NK2 z)A{`W{l;@Iym#{Y2kyInxm>(zezu{v|NSgBY53N3NZN;E<@MHUsr%>9)Q0s9UE9!l z1J1U6!3Ip)D#eOpW-D9bRu+(Q1CDx~9qM#cXH;pxRPzR?*AN^f?ty5yxZrR=f$upg zhB=vu(v}xFF_5^>A)UxI{50gSY5-zG-zbl)&y));e8s}|$EPNacf`|kf!gNJ_>r0B zWPqqtilm$j5D6x7PRohbhMW1AA{TZBoCTGCv61HOVIK2innRI3) zvjM{|K0`AI#&@QyF^y}5NsdH_-3{fKq~%3Uq$@7XiG1^;;<*q3@VLs2>nK_-Om(H1 zGMoh1via*;wx=kl$3vHodOa)BBZx~h5UWF6Q{197SB51!8SL%tz4ZZ@U7*?F@5G|g+?0}#1=uj;VonlQ zTvjU$ppO5HKpG}RR|^9A{4g0ZoeVwO5Az`a9w`^|DK5e#8i*CQ#9^p`r^IV~jC-0l z1S<>*aT6-UjYu^<#=U+z9__)C{x?h($Mc7#QyAjkg1kp=wH9^XK)bfCE1njHHhVgq z$#0K1ukeF8`sU%ft83Kz>K_X$N|CP^GEPH09QMqWame_nFEx+Hd5vNT2ehVw4n*>_%?pvZj63 zZ~Nea2{v&Ml|70^`6!Qfd&5LTKI4&bqVrJ75h!)wIR$x71PuHVz;ZU7jp3{RADWyj zPF@4Q1%NOAdmRtHD~!8kTJ;wlHrsv80{%b%+-m9o5z~s*@A+{<1AFu^JnQe< zU|ZMa+7X9>8O;U^%;s~LEHn*d z>rA1p92+pa3dYxuIEF=TsFwwZ5aG#fj}%mR3`jXzkBq9AJg1VE!U_{7PBn;dB9k~Q z9Ne~7ILu>$cEAK4r-pdoaI0V3=c>^$Rltmwi^XU{9<-=_V*1KfPQ56tY}M@F+&d3|(?EAkqlMSP$&^v)Rt8C-4`7?t?!P3qEUtZUK-aGj29j)ETmEL72-E)&R2xc@1(dnpYCABG-qzHyd_^kyojVvXD@h{(I z;#SWE8AjI10?d@P*nsC%aGr~}2#XrFZY79Ba?=rg>Wu)a9#wJca^fOyOm zCMKNT?Ry(HjNqv0Z($I45cFKde6UdDs|Uw6l{cb&wnp625F`uqPV)YSDfKq z&l?SLU9b&u;gOgd;dC6IB#FSKqt=0>#Wg}DKD3@p*X{I?+$|an-K16ZdholiM2{qyCF`D>godlZ zC@OMEZ3@OeYAa_!3zhc_!hu^{2PD`k|y^>{O@ASO)S+#Z1ZK!9XgPI*4aU%1kIlGN+bG?ei-F_7nq4|&70 zykSCi9^Mxl4imd^?CUM0@VwmH10b@1Zv`HI?U|Rn9+C3836v-uj(LrIUX;)C%E=|K zoTE=3t6$gcqFsMss66L-v|onIEM<$c$pb8-lVuhHL9_ts5L#8QC%^kI;X+zqur@x|b6Rk~so^NS1x)U|#|LsP%FXI?%9g0er7p*sLK>t5x;-^84~y*T10e zH!IRAUo$@jrPk(aq8k`gP{ zM1;gZio1|M7i2Vy3{KbadG&D6FjfG0UW71>0oaCL0l>oYB_%c9qYms)Si&w%=*Q}k z;oiVfA2)xlc3=Ar_o)Ijc@>~_p$nQqLg2(ez|w)mioRs&EWk|E6nt~<0#AgIxr;#t zb+`iD_qKqsf~gkPjW>+g8(A(=)C^d!6I>*iIT8@V#8#jjri6!ebx7u*vhoUFgvG#2 zLumQ1H}t8x@F1JN$xz9Mm<9+Hd-)0gb>c!EaiwXs>3B{PIp_5KXH7liM^fWHvME+L zBDBw~FRxE7g(DuMI@OW~iw8|5XW~+&RA)j>91%^eCBjGz7leb#_27W>uE_AcE#^ZK z(_+{(KMSJChEZx4v;fpGTEr>xNQr}+2t-`**fB4I`+1rQD3B>938N$2q!##n*c$=N zEf@A{&tu#+I9C8SAU1X(E*x{Cq^2V$hVv>mWKy&jz9#49Cm}6cPbOWJDU` z1cKFaV-Xg_Kp@M7{hE_MJ{p{^C3EW-DHkw;D?oq=F#R(Cd9qL%e6WeUo5-r50#bE_3 zz)Xw?KF47&dvhXiko=ecfGA&#@cpgetdIjSEap#ZKmY?p!?*@J4j|Gry5weZI=|%Q zA~n0DlZi1&QM-XtFvBIcfQf-YkS@V_dquB3fEg$UH^0_)nEr2Y4je_zT9A)Dt0$Lw z^m)~&>b!gfKyAzUJH-hwU^)GD^%GT;)&LRsjSjH5brq;+_f5rk{nu*M#r3@v{ z^K;2VWF{4-IwjAK7?znB_O%$S3q}|(T@ncC5E6-D$~MoJdtG;3QuZ2m`=Xv@Ad9 z#R_taxD<;TdL2=6Mwhzrh=7()`gAlUa0uD4Oy4o-MMug{M=Q@km4qskoPjI#!7m^< zDBL_YIzKgvk;>~~9p+ZS5sZH)fam%k86c*l-0*J!P%(XuSbctaeH1s(={5(Pt+Hml z%kJO3A*vWcqwCbqBPZ5~dHZ*p&$9bDFidWI2Gj6R#!GFVc2Z*Eu(}yH2HX0=1ei%h zOAQB5aD=zV0FZo%GR>f`cSN9av+kBJ3?eeC5nWD~Nw7J);EzFQ!8 zoCVMnouG=2dPzMpsFc*1mjBOP=73_ZpkOUd+&strZx=;3X9gJ+WC1{B5P01u6aqOFOT?m972QZWo)0u8yR4u zdk`5ESzW069lVmW5^n5J3iG<&NHAtD`LM)iaZw-|WG}_ND_> zq{V%-g0<2NgeDteJ$g+M5QWIa_~`C-<07lt-7*_APbugW5KhyZq(1_RgUZe0@$tD4 zU%>f#j8uajYB`waswD@U7VdZf&{CL|<~)6ytGT=otMtI0WL@r&v|d#Z?ZD>|G%y`=IaGnXWsIBc~UH$DQk_arM4b;RMC-~e*Cavg1& zsNwIgt@)_{oCR_i&+1_yz7ddt5yIV3C{{T4F|Sn=lMR@2C0Qqe8x1iKs$zgJxZzqQ zdNvgP_mYkjBE}MHWFR_D(g5RN<@^CB`C-)Y0w54CvQ=&x&a7n-cn8*vr(E?50Xm`V z14-AqC=VU`@#UYl?WaK%bUSV`S7T+OBpe7wqey@i&)u%ljd;H)g)+)kqpmK}mCPQ5 zt5gS2aI&v?Tv{_=9b9U}NCX9J9OYww-*pro)9nZt4Z~xC#wIWw8HQYRWTB0kl%F3( z0oj1x^u^jFaB>LIbl_q3aX1A;9>klI!x&L8dX>gw6IkQ`pf1-LhdsIc3_#SjRfpKr zruA7V5qr>ENvA{}VirSalxn0H%Y&x4w-cu4acDhL#%iM7&au^Ug|2~)d*3_wTC9A2 zGY>VaI2BjOj`s(5nNq|d!vPeW>}wtu@{$Eu_kln9SNad)#_(oRIm;A|-gEpa57yu(48FNO_1uPGiL5V?9=T zL3R6%rRAtR3(MY5RF3MjLWK}7$mVMVBT+!bfXkXk0*t0Ng`>CvZ(SDwkfG_q#}(%e z+4V66OKg}!YT2`+%Wn#x2I|ud6rv%EkSQSEX$(%Lh^>l7S(pIy9ccMeEv2FW5)xt% zFgeS^8=rJBd`?o-dQO`^UC3OpR{*fPqJ6m@pMyWth86nrpjb)Z6k!_;wpFf`Bv+=t zj;5{qHjx7|uXUN$D$d%rdLDCL;{z5w&1LF!GoK&dGNLEBQYX)mxG?hfy(-lK6daw$ z7_v98nlA!?;jq3Ik`YIz-lU6D542gW=&_&7X;f)(V@(jllNl6?<&F8%NW4+nW0Ej}x6fWW*U6lI#QqFp2zNtJd z)%UMO_OriEOO+N=$WaKj>u(Z?1!s!RxUo9fb5?JblL=(pOwpU!oF2i9L^pAGu>9PJ zcY_geq9I2Tw!KCtmeG_t0LtKG4LAx|)Q~zc+F={Ctrh~?IU$?l@kuBy!c*OJB!Byn zi)(hf5+bP%eiRCE3KQf`fzA(z0)4`ZF3eZGF-{Ikb(^@s<9{tTz%gjP9tJANdcK2a zF8~S}m!WJ2!Xo%aXR$mGc-*^LyMGOGu1trS!_H-G!o!xy)tX9f0&&&02PnbloE`nB$#9pVXLB}e#GUGp;7&QR_40t!FOG-?J!#(fQPA|tlhqVr?LA(FMos`5 zeCT@wkVA)+&M>ld1WkqI4(BWee{2q^=s3kimTHPMZkB2c^3yma$!v%PPK!?=;P=wpcR{)wOtWLA!jArhe zQsS%@E6+pIPHWUq!}%~@|D1Au8J&!~`y5uQwR^rbFgo?36FB$ST$n^R{Y1#n8#Ppz z>V#Jpl1$E#4P*!wZ_d#sssm-P$+?cW3^pwYfDVf~QY%Fpa0h*>X`i_owGP}gJCXU( zp|~ryQ=x92ST7!XwQRs6ss@*Uh3Wi^ANjPPnPg&}O+G`)pd&ZCP$-`(PO~CyHFQvq zR{)}7dkwLe^3;0CZVf5S4`->jK36QE*V!M+k*lHZm({JVSI)R@{c?66#WLi?eP}%f zOUTSzuH;;+aw0%9%;h1!{qSBRNrkCS(_)OIw+J5%bv!N^Jj7SA$abI%R`wS`NC9ut zIRSX1Yr#^>O3DA3J_ed{Ak6a2|Gi%okd@>g!B|nQ+?d&KJgAs0K}dFdwoj9=O{G!x z$hrcp6N$4NA7FT);{jxVZiBSda6korP8R?)iHmT=u9cTUpGDvyTj{+{^WMPsu9c1M zRcI+?HySiono5JdRwVjX%9|5i(cifyn!oINu8QvGTIYg9YMXC04IzjFki%azpqa~f zlq(uaERFC1f4GYPR1LL()qD|vL{~7(4||8Vpk>GMe;1|AoJbOsj4aG@&HuzlWTQqR znmEdpC-6->lJYjW@#AEZ0ty`fEQ6PqNMduM{2bug9AA73pj-r(*|g0%)hTt#+teKU z<-vOCr_)3knE2`38K~Km^B6`mpM2f=`OMoFfnGGWL#$B~hk=;c)C0iVd<{euqB_l7 z#XLkY5hof7s2{io|3<7Spn#RN0kDAeC3pkzfrDC7&jXo(2^U2nX zeo!~XupV~aIQM#wqq2<_2}UtCOxDj_pS1RJgrXD;y)S&eH=4f{^TTYISYA-ol2oOaDkq7r@Re0hSqCH86fR_ z>3;uV6Jut`UADvnP6?`Nt4?z&=jVYO#$#qaeV2;>Q~}X~uB;8QWqewf#G{_nOc}Ny z$Ad%Ho6V8A80ewbHUHpvteOA7z;5<*?3mGOkx}N0^)eLvufxq;ELePf{Xx4y-qz%R z75q8A1%Q=__I2pmD$q19R#EAbL-eUPdl$=d*%pptnF`J03Wz37XgUHO3aNO=nEWz zh9j_z0L<3@^C7S6J+a{s#K&-931YT3w)8Z!@H972%fNj>b;Yu|Xlna3-bgW#C#i?l zHo}#MkdLKIG!wGKCt>FB>%{a~STQZIdI5mKx+Z$-Q&2K9*AN;^ZX@O@HB%>T->7@! z-AMU`Bvg-t3|w8(zJc{+@#`BU^H^W1x^i1F-7W$!Yn!gGkHPU=I=71eR)?=GYrus- z*NcF7XO6V(+?lg-;lPm0gh}8$ST4GM8aG4grbCg2lhU!>y;O^3wpG+U%YY1DqBpAI za*ht(YvTuQ#8b6JMNnsBsrZ=(b8EzVVZitN5TK~^90$!aR%%SS^+i!+dbwoo&E?gL z#r%2=vQC}7ig(@mthNoNVp+@PDm5isbR^CszrM~1EI6VEt`NvrtOT2jIW$T!90lDl zf4*V@2a2d#>fpnRfH10C0Iu{UsS)uNj-J=?Sbd4MPGnvrGaaVIKzEnvrfff$=?6`7 zRQua^{UV;KEvj<0Hm*^(&7b-J37!7dUjSgkHhF8y*SI>x!lg$2#3+jO3g&aN6Q!4O zg=8KG<@_s*?F?__6e>kQxa}P0Ew5;DDB28ebD@JdV|;NO@z^jw50{T&iz!U7>qP*N z8aDK##%M#R*F^}JjN#JQt|RIvIRZqns5eZnQ=b&cZE(41nbXWazI2;d(o3TW|23?0 z+5*W-v{M3jkYf*Om^rN;g5l;4c98p@ftXo+3m_*qoFA*{b_&bftMJc@OEaLA2=fnC zuPOI2Ehsz)c(mVkVZ*bOC?~Bc3ZCy=~KT zgkFby{$6rbQhUCk=1<*~z2qZ8REv)QTq6p(VP9okE$@x;;ILj?Uan(g)l^aSA^Q;X z7(yddwhT2-QX%B*kk((CHggz|nOFwa*K`4p)Vy^LoLO7D)6#V=szv=u zsm7!8GvJDwGNyA$Jr${9nDP`s0|qynuh^@mSLabtR0zXLaNgm}cEer(AF z09HpO71wc1!Bz9G%8#cNt=1sDi8bS#>ljyZ4%oJ?XHWUDwyPTSu1F~cL*pA^fxxV0 zZUl^mOfhc?ju_~iDntP&u2h_Xn3>{Y4}cZ?IC>yAEbuMiWT~9VG6CuseI*G)Qq*fx z{su0SiA+%Qf*?dm0L9DziP-&zK52i60hxTRLe`WyP_49HER3# zk2qSL%@+WE4r?>8TCK#jC5(ji{8&-c$B4&=x&}uKXSY{1T+SAyj&*fxS<4wh57xPr zGw<(qSk;zU8#z`SnYJ6eClpasJN7~-G;VKVp2I`#4lB0wY&0I0%l?!z1A)NCcwBsIEiRp5d@?9 zR3l(!sPjp&oSP(d4T|2q$F}aM^RKjw`mb`CQ$9w@dg5ny_^`fQ;PrLDr4RK?yuqZm znGs4gTlJ6wBQzq6m3E_UzmT3*B}LP?@o*5>W*FX?r}_|zHb)ye{pSPTaA>YTU>9;j z(hW%4m#_v9;j&C7H3?{ssdh9EfYEG`!Pkwn{do}zG$G1_gD6B){fL{dA#(*CN(iA( zn|GszuFSolE)rl2#3{LhMnf*(?L@{$x*09G5=E1Is0tEw=uxWOUZ>-x5=x@r@{bUm zcnr);RMqRXpiZv94D}QN62AHfl9^LJY`vJi zsa7>PvNIDfc`S(#X)YFFcr{hxv|Og(5i}oi(79puWg<`zX1{dj#n@et@y`bYxGgZ& z_A;?yfbgFY2r4m*#vI{ukOqR16Q7uUk09<7aSf=17sEqf7eWzog(08!3Nmd}y${{y zscV$-TYa|x?oh&q7y~mC<9f{j)ae2sB-~~TC#O%lz3RLMDTfx8RP#C|*2Rdmb)b1J zu2t0EHlF=}md65_wlREm&u@IACyb9vsCV}=3R-lj5qHjxBVsh{R7)=|kQgtnHLls$ z!#Li@g@Q9!5l7F(K+Fsx1xvxBOPhn-L)~*PcuBxw?L{NL8bB8en3XOD_<5vhXDc*d z_|MnrjVy%_A`d+~H9!Zg<+?`9NXKOjnrXQXR3!v4(o7e}LvtCv;`Z)@ z8u){Fkg9gESIKL30q1NNK<#d&V1LTh!+j@gb9e&C3=k2B1hpi*&M6unn2EK`tjYsof4QHr% z#t#Y1#tzp2_=*6%DnNLi$2lQRUo*YHMNgb+O_hH7{^IjN6w6_2`B z$fc7*F2-l3&T?MF(K=ug6<+ck=w^dQJ`nNW1h#wpF+%8v^T)h0V10BB;e~)X$J_;i zJ&(l0I+Dvbnrw#)0TkrZA_6M@o~=!GrlZ;3%-Dp(Q_MbiqmZQx_Wufc>5O|; z)wc%2%agm;m)Gjt_kvWuOOmA3A2xb8XHL&X^CS20qL*!lpW~(C-P3^uZ2rgJxHY=- zmFuI$(dlRm7XkQLg~BXE+S_@ZsVXZWbPqSx{V2u&rag(n#Flx6#rl z#9igcf@Z^dO&Ks7Vs=ko|BpY^{ZsyhSCB_{G21kFS@PuijmL^Pm3R<^3`$R{O-?}=XWn~#eBNpod9~XkH@&;=(TUB2c2n)n(^n8`cVI6Ul_ZYFx0v6CB`TfxA_i~s8@e#;LQe>}cC zU4Hw2#>F2K=!9wKcv`>c4)EN8x%KOR{P&knKYhA<_`_c>&oAM96Hd2h_)Y-5ujkwU zt%eET;&%j?n|W|7UmT`5-gr2f9#PdkP({@@G|QZn26o(pIc>{}V_|Oj58M1B#~wNQ3_We0 zj~@4Y`*zN|kE|igZGZgo6($Ie*H51=fAb&ymTv#EE?CazvjZDC5MS!Q{)fM#4JQ7P zAYAt8bwLYooPaTezEkB!m6rf+1o+2A_La;{-i*2)sI*MK4G#=Z<4G_v)}&u^7=9UmI=&5+<%4#EC6Z1%Jz|87KGZk5#UJ7i3^*oYRA18 z_?ofTk;~ZssQRH7{SARa&CILXJ2#jF+$jJ_hQZ1F|U1c8;Qz&-g+E~wH>p6Z}=ih@4(^(7j?J+_}Q<1MxWJIcBI?O zIT^m-4useESAYA9xX_1t0C)o+ycY2}T?cS9{NR@fx${N=#K{B>mET7$ivx6qI~^Ygd0?91AqWAmHv|&W=dfv30(M|IdW#Q zoKQs8y5WbE%)lKOa(ZwO*L+YJs+F=C03Dqa4F^21!5*2?22yW&YOY2rOlJf7G4lVMK$HB!Se1lDiRFL z74>dmfvD*8=9n2lHpJZP-F3i@%CA2Gg+Hp)2EcdSstxK{jMyip?5*%$%~JMp`)m4S!PWI{@g476Y|>%cH$-x$m8)&!;<~dN$bw z_S9^X6n6pVIFl8j!kR98zz;SX^%w>-W zC^3>spw1ltM2U+4w+o3^vpYErhDv;c4jovVZ+p(VQq9&$>!xO%^Gy?x;CQi350L49 ztxMKZIy-Pk2g0-?>hLZh zs_D zadK3iXV4|E#7QdGt+D|?FQIT9*OYBu9kG+Q3g6~bzzK1VQ|HY==JIDRJbG-}zPCJ{ z>E4l7_|h#`@TNb)g84sI4J&n@KKI&z@U9zYV0h*7=l&sIel@!@l$8uJ^H`K?g)=vM z6J1bR<}k|P?DiPakgTfbxbrOh`sO&t zGvALluY+^&>tQP%`v{GG8*-;+X{HE=&DOWk&$EjCI)HO7eh}bJQ&hSJN`Ysri(n;# z$b*$QFb1+NKWW;Wgu*01J8IjSQ`g5SULB(>lW^rZAy$G^)JWX#hU4t0+DRq{6ECS7 z?g`Y`0H8kt4Sj=j*0j!@XOzlW=WJ)s5%S1wuTg7@l_QUG-4m`=SDEJ{S6WtcYQ6xH zKyAOE4&c<~A5%8ZY5$d=wTu*Ztu_I++0nZlH}g;J6IgD-vgp ziR{I>TogI#jOt7}SN?ZbQ0M*BSx(E@fh%@^r#4Pfl39T{=oy`I(prrsgjQ&vS_}e{ zF(&e(B%Rk$F|NYfD5PN;>vn~pqOO;l98BD#uDg=O2M%liI=q|Bn3ySPGh_PG7=hO> zeM=Z-+&Awn8TEYcBUA2MFWb|8cHqG~fK#2PyLs`sN>i+cGg<(-LW3nBqm7Z3=dtLM zYDwN-Wnyuxs}-V(nz_rPb&%LrUAl>;0R+?F1^`JUv{3IS9K|1az0A?F{lP3=gzfB) z&%ph8{7TLW&JHYhfL{jYGQmn@H3kz#TeOFOa;0`~7ditFnzt?FO;Xi1OpF@uScR;j zW`6%@olFka4FIyChe?A%?DupOzt8Jc5__^x5_@{A3v>_b!hLwy0?x$G z4h(c)ah_{8bE&8rOfK3*dl*n|&`t!*0jMlMhVpR+UTsnL+fPn zuqNe`iw$6An%Dqf5cD=F<&igzkK*U5+qrvI_8corqdm4Umd8Trnh(zWOKIuBZ9M6> zbs$YcocoLOpWPB!Oo|OSEQ}FTTC|sdQ1A=|tp{S30?dXR9b36(DNR7 z&^nrYtSR%;!w%3fzte#nyJn9vqO_L-*`zv>f1cZ`bi7a82#I(O><;*Eaqul=!+>cx`Bx;xf@Rnsjql?P-8Gs2Ot-a;$+1*4rr-Qglmg zR?rC2Ff}R3n4F88lBq+}0anZy&)fkh5^%x>E_@zAI|M_jBl)=->-pC_nYk=cTWa2= z*ZpX8{`ZsBnaSCKn{)tP4)~A#goQMJZQz)HCbQF2#brVpk+%StV4gK+Fb+8A0s!L? z7eqTMMsn(eTT8hUxMOaoWSvcZ*3_mUYr~;o;+SRwFc3|he`IHx*ox98w8v#(@E15P1 zk|v`TUgPYFT&}wYR6&wS3z0J!r+hF4&Yh1RyR8}y+bOPN?uW4iCRc0fQ>7VLVjEm0 zCU8t|0BkgjWGU^)qIuNFk0J3`@@IgJL(T-U9!Y#dv^ssrfinpq>S&Wf4`xY?YZ?rJ z68u(Duhv`Tb;kWNI)L*JUMfF*_v7XB$4{5%_uXj_D30?U1;w2x zynZ;T4Z<8xr7B5^K9&&W?};6UhB?J9K|5>!Fj*u{VZ8KbbUssxQI?Uh`ve)t8#41U zfk(QlE@j5KF3;aw;ec#m>PMLQ8NasF}4r|Rs$9vy%w_6qO(|N6UcFVFAbOMft? z#h<1gK7|Ku3h2v$ctwPd3Q&gmtEaF+vl|>WLaBRVB1_0Bl?O(*szc&@7^zlcrGQCz zg(a#jSEA%%X^ADefF!RF*5a z1y`=4468LP^YO~bo#DRB4&ZA8rraO@>Fdj<4<9e@zIq=Npu?FM*ZX>J^mGL-{ZdP& zBw0kAwrbH-2+{#vr`$nCI#!zNRAL!$YCudThuN_J06+jq zL_t)1nxR5m2s>^7kQ=kI;fsh{6>?kKkK=1Y&_>THS+Pfpwc`5d>bzub#ldx{uUFAj z`b_`SZwQ*FbZ{ys{g>VWo^UYtzx|hQE`RygKVDwGdIwjzr_1xxGc5e^*Z;sCJ`8|o zwHwf~v{Jv0fS*I4J(-eZ5yCPjXgckwgKNE!Yt9CAUxPza_Q{J@`mj!)2AmoYn?nQ0 zSpa0`4FED@V4DiZTRK*$=HpbUVq*1{v8BhbRN-Sd<)w2=Hf3KS$z!P)Y{KI!{NPc` z3VIV!Z&+@Uz!`LN2WVE}LLR5*AOGjq@b3Tjmv`^*av7KU0J=OqhcSU8emDV*he?d{ zm>>BV?HmMnMkP0ALi1VAJB* z-Q$!SS2E>PO`*qvFZJ~3p%d~=N_qLZHPwO}pq#JDao+~_UEh7Dd}il#0Ds-@`)~hx z`S$<&=JLazzQ4SKZ~Z?#Kf^nJn8UQ_!yN#;9>|ntAsp3>0EgXCu{)aJwV+=P$&{qP zpivIz7F8Zd(Z+Rv|=Pt`CS+id7#)9~=|ho3G#{`H5;Pv3u_1s^~2ryBzB^bCtX-TsHa z{zsnx03LmUJCnpCWwMZG=AJVobCM|3YP{sUP4d#&#&{)fKk>Zpl#CCZ$a8?81F?B^ zz?@kHcHjm8BXd-n^2ok`^BzA|VvQ3lA~`N@cd|M&#)QlaZ)W)RE9{-B-~aRPF5mz8 zhs(>m7nuIx_AqrNwni^HyWK4?)6NX!71+z$*e!)IjSUom5=VTiGe~$u7@_kqIR%|3 zfW{-<)_;C}pE4(U0eoNbwf|@GAh&VL+1#arm$`=usXx;G56d9f3fRvsj27 zmFQ-)FTj}hCzYJ{RV;NEfwYA|f)32)*8y`v9oWGe0F{OMG8b!)Ida9eUB|@ASvfZA zZ4P$CnDUsjl!$xMJXW?xckbQ$_m}rS`ve;R{IVE4;0fO`;#E5>_@QUKU>ujz=#0xU zK(|F#%cMw@Xfgh%Ijanp8Te!st=Wv>iNgQsqy&pB@VM~D+x~b50RQ%XICqHCew9F? zMqhp`=B+7^3TSdB3$Y`PUV0_V+mLE?r|R;)iKXu<(7G^4G61uA4me;^L$J+%39w#% zF)Jw}TS~)w2NQSTnlbaVIS1B{u1b9d2MCM zv*PRqXH$Zo623X4#Q|?2<03&D0($otnuCi-z27@DkS9YkNB}h2R$k z9am`(JjfSsx|I)sXjzXNw{Q+YLz_4};ve@x20#ZP*dnQL>V&%~X)qFX9bz=+kW(qT z8ms3r|JtvyJ2|>4KMNOHQFb%f9>R${K{@~}YZ!3CtOj8@(EM}2PIY}%nQ`BTvR!f% z?%{gLgVEb!O7PtT&MPOpZl30M@AFhPj%yWeq`g~rT;9X)h~r`b?;P>-WPHnr4_s96 z)H(|X+4p*ynm&VPe~PnH2yv|u7W1h|(=5oR);H*c z1rI}gAlv~!gAFf@YzYf4mW~rzBUI_21lHTnZ`@}&SqeVQ^>6#Tq0Qez{t#(CQZ{wk$CZ@#UJ3b z-oug3C!Is&z_SPc;f_EaCm00#UEM))|gDCFkmQLvFGPN=dGSmLb2l~61oZaQHi zCkCc*DLiA)h0_Hxe>4FKTKv%rKeXTjCw!xWP8To!e8dD0LnoZFvA+w1G}VVTx_Nt^ z+6nE$jviTCmxpSj--vaw=s~ygYz;bccZcD9Am*aJ1Aq=?dY+1N+3(27I`Q`lQepCA z&t$?3l>n!DhYsNR;2)&p^TWp-LhM383qxG^X;FBA?*ia|u&{(52=fwVj9x@`30@=F z)Ffv!u!+w}vfwKej#H_U*Wr+R0O2$F(W)O8^GF@f5}h}kaeN+4WCBTKW`kqmXS_zS zPL8>8s0Q@T9Z@&zjNmW);q`B@C4;Wz*%)->G^XHvu;c?!wE@6r>CweU08@N@enVP3e<+O7raR`AJ;H1P}KZVQ7I92 zLk(Amvir~g?dXDYrFLf9GzmMtk)zj>$#rMY2xA$z5{@ZJ}QEa77ZH06W@9s zpxy+5Q1qCYfJMMS!i%O{pf+);Ae(9B|0q~^uHh=jbn z%NVLXBZfq@TW^pBf)tr@sL@PF}_$Z^$3I&`G6X*-b7%%1to z{qSKrU$PmvTFkZHq>MwNvVg~{ZLlLZO7$4YFBZ(ib(aX#2X;c;_! zq-T#XS%b7GKu)$e9?A2%)MZx|bDVM;1Do6`F7MhChrJBdVux4V?jZ-vo%2BXmi@r^ z{$nL8U6s$exAyuG_pV@=Pf86!HjEz$0Gm2-88U`afZ^a3AO_fxj$RkQNgIJZd8>Jb z4u$F_14dW+RZO=%%+1FH;y3(BlS$fTDlO}nyJ~9|(GERePG&tOM_zzQihF@?D8+B- zBH^^#n1ji&<6=JosGcBXF|<|WaR#pB)_GusoD0hLkPqgu_E^nccc3464&CRwPXtzp z3kBTxkGF{1b@RJPIR_|YF4?eBxGYz*Mh#Gr$05zH#$IV9#Je5;3!hIWS^-pB&w`v3NwClKo z$+hbi0D~I<>=-keFhwid)&jnu7|TNwvxnC0C+FJ;_wGrX z0Vp~gxrV7#%R+p={Eb1D+{c|H=OGsh>TUppT$+glrB=*W6S$Cm zE`3BlX^u9J+#`BF>TMBY-RX5=jODhK9vU4RkTppU>;2Id?JU!Gq#3*vl8%vcam0VZO?-;%0*Ob(*(KjWX~oe?U@)A4t;9A2 zbAx)?J0!A1G3O-t54riE#XkkiWRk*M=XXW#oJ*yoin?2g_Z6?=D&J*^04{YDe+cfl zB)lH8?zpIjG-;g_3+19S7FM@=kz3g-Bh$l1ObwfsBX7unUkL;Gv;uK?T^a@=Sr;54 zf(s!dTr<>D(x|~WNVe!^^Ux%02|3g<@jTdb`FIHFayErytrr2fWAaN>b55p~A=fsj zdk3KCrcp4tNy{LvHyFs}25I^sIoGl~&2LrvWR|2(ZE@YvZbaQkBLFO2|BU#&RSZvq zu`{Gm?CEp{VGXn6F{a}1z#Zs=B0O&hsCfH{Oo3%_rtIK}y~pDqa-z08z=Ug=Q%=T+ zK-!|Nc{0Pj9$Ph~oDZmO6Xl$rmFTlNG@4(~y z-L)obv0-peK|DWvnwpIFZ^Sb2ujyi``se;LAIuO7bkC#pXUO9xlLq9&MijGJ}b}Wk@%0w zC`Q;sKksb%Fzh}Iq@p(70Z6S65(YPE=YW03%}}}#P2j#cU72lW!(hIY&3WoJvFa} z@YqY8Mo-qLU6FXwz04)o{jk6p_XStW?=$)TRLl`Y zd^LyTgiV=gD=<|&M{>B?_X9Qn9E+1PrLnPnTySXMmqtG zJTjxZ=FVFbo06-&8m`knjB=ewUX7zV?$=bN2xiC8KoQ4oQ}HB5^rHC8B*uLNPRyZC zsT>v{GDrHjjQSkN60J1A03N{eCnv_Gw*p0MIbGOI5jk_hfjo7q(ifRh% zSL;yhwgE7Ox#2l_MD`tAmSdEh8?u*)m72LMH2a+A9i#K6G>Xijc~uS949ZA-WI2Q0 zY>4^Peo|80li}0(9YX}6)l{mn|9F2cPHHN2B0X! zhUt%lfC~~u-}kBr&X0p`&vTU_&xA9&%cCxGGI8yiYO|O6p!bo<=iVi34F6!R{5u?> zu5Q~9u(1W~-i?Lnc@N4#do!a4UeI-F)X~xm(b`T-PJP0~GBN>b2S_v=J(NpkvSF$t z&r6Y3{T2*fs0EtGaoMNf;%rRQPCJw>9-??EzXMPtUs8{2nq)e|CU897AxNDk2Do0X zGm-Kv4{60HsZM-2u@ZX75~1*>ZPT5Czs5Qj4PU=8)9h)oYazYgZBs%=gQpok@HLzc?Dd_A;&y12`<7^Sf;}x|4b1) z065gB7d1?pD+duqf5>C2{`G>2&Mk*ocC^V4(Ik-0fA}K+8!;Pc#>_M~SxSy-eU9%qAFdb+YxtDFN%Hk^4 z%7%l~$y(lDjIG`#XdL#A0F2t!7G{|~C6b31U$b!p57*H#%^(HC(nbLJS=)%kaR%WH zCNOETNy5xE-w+^M20_(MkK>qkO7IXGcXn)Q#U|&n#$mbubn8*9a~psjnTL?p7^f`<&ax$E|Fe z5wv3yIG)QbkXVK;WzQU#*R%%*Y@lG;Y7A4Uu%~A05)`@AJeZZE)NmF3g1e4nUwPWH zU%U!aRC5808pwg6e9S4L{@fUZMl?HG=^eLr{J0yD4LMAkfX#X>JB{jh0A`thZo$P# zX+)I@zGl>s%vLaCebY5R_71;RudNU|Sp%*#`9?mvgrdHUIWf2JvcLx6X9Ft=02``D z?UA#&P7KnHE)bOrMtR5qf$72Sldc**wHe@en1Loq87)ehcq4gE6*H=B40OU3R}Hxf zd8qY=6mtk^=;<(Z0uJ?|a>if-kcf(Joj6Wgf;n_Ms8s&@z8ZJP?Kq0j^PszPTsHeK zyESPg%8yh~))p>h?-B}L48Y1PhcMI`G=Z)+duSFHh)M>rQx(VtcEItlQ*dV2Nw+-G zeQX9WD=sLM7oHc2)9xOnwu3yfNyuDoGaRU8dtz4zG91DJH_ilVO}GOfOdKF6M7_O> zS7PA&G2BA-3ochG%)b^41Rp6$cWr?3st}z&$?&9gS50Q8V4VNneK)*J&dO@;3`9T| zY*-kr84N%V5X25z-;_1;gfqKMxac&r+wx`r2{n-puu10PG|A3^ybwCvo0YFv_*AF_ z-sMauI`|HNVoah!Xuw0A_^gIh+(T6%$&Tc&OBTnz(4$_Y?FU{!aIW|+g?|=k?!tv@ z*}K1n7s*-Cty*U?>?HKs+YQBlacV`w&ZrtBV3$F-3MQg=>t6yapqgdZ z^omGB>_eb}Ix2K6s2V35N7iCneV&Q6?l|%i-yyh1i&^hLy<_o@LB+k?yLZS-ocP2+ zrdpyi8Y9ps?QO!cL4yroWWXed9Ux-(0JIfdyW7pcWF8zu6SD5mBR8TKQ*8iDYydQeW}i!Y4jp4IIq7_RvaScQhX<5|RwCuu3fZjS1>cF> zqsH`hqR%Qt)?=8nKm$Y0#XAnT1B83GXGOxvq^uZANuihkRbwO?rM-oel^Q6<8G(t= zr$aUY?PZ`N1=Yo5(1vibsslD}08&9ZNYXmll&VR1NpQ4A*T%8#KIz_N&%MTQ zb>^6t>b1i-%nfpS^bR5NK!`-0=Yd}L@xbinGR*75IuZj*9%{8>;bc-)tP+~pG)AUT zoCbyGfd(w+g^o;!a)WdbhAg8G9WBu7AjqN-9oF1+(!Ilenz;jBpV>Bav;-4pc!-@q zHblj*51QHlj2EHC{BaI^MH+;a$;vD#c_jp5XP({$2?jHp#ybt z@`B{j^I#?&KR=}J_WAnh;_Lk&U50>jJNe^vj8LpD~ zFwpqR24MFhs)RbxD##?sgE=&N9dn$|Mc3`+)cfakJfGX>YPC~^`X8m~mAo;1%!7Y6 zQeDlRd$&hxc*Z0yW?w6Zy@`)WS-D2(crh`W+T_}LY)+WVU|xeTWSo8IdfB)94gp#` zBzp`Tbqy`}#xO1F&9>1RHU%n!T%{#t*h&?{z=}WobHKY6G))!?2yWBP)uJBLF!z2= zckbAF$0u2F20)#5&M`hyRmY;EVqv+efHGISrh>lt6L|vXMr2JxGh^-;`3-$MvNzn z9>T!FJk4N04uJ4>5V3*_d5K~$)D&zmuR+*U=nA-2Zhd2bL^ZEUkT<8+C&(6ev(D60 zvI(YFVW!_cT;BjRy4EmFWURZh5937eHv?^TPIvg41)G0tBqnjIT&_|#oCNv;9~Rj< zAy-Mp3$igO<^HtG?oQ7JMed|dRy~K>aED<=HJF!%A^o8_rTklA+M5IBXfZJ6UXOXx z%RFyhT|Y&Y^b}b6jf>cixeb8RK}R*7!7ws!#&x0&r}v=1Ru}V*A68;af(|OE5)TV~ z1o3Cs-Ozt{BsoOs)uYcv58KLH^B`^u7y8fgVa>J2+U7u4Q0JChBmAiit))1j*Lg{F z-+uz?6KvP^+7VLB6P=0Mi_qiR61$wIy=YglPnR_9FkOb6r5LKe0T7Q6D;rKHu#ZFD zsCHIuYs_c}w3dCI?Gno_B;%25?Km_I4hlh7;0*eeyVH%m1XUAd_! z-{`R=g-}jj)O;aZa+r^HfX8$TrX7}(mfmI|>l=XRA&$$T_jUC1$*Q7|88RF0n^l`_ zOK7RAME?cd&G8BA1(ax_CLz_KR#*;J#PkT_Tb%m)>Xt{ z6}bFj3c$2i1-^qikJ8T1O{(LvLz25uJW#5;)l5YBY3{O5J9HUa068y>u*C+TaR;vz zYd4!Dy~1dm^Qw(;h5aTzqgglK<>ak6pb{_7UBWVPMuuwsIiQ?%N?i2uMTGyu&+F&c z%hT)2<@p)j8DkBwi~0P7TNw~LpO<5O`jNFjo0@W;Cr5a68beFGfG{uI67Zrf1`s~O zCPy#wT1FiD^BHhpdKpOzOP+jw}OSowguOrhW*KE^NtY#%(EUGUQQ{jfdc}|-`BAF&sWVvZ5 zxuZas3@-97_=J6bp-lk1^`{L0uKD~6pSqlpbQrHnA8{)%$g_=uGYQnh7k8k<8x+Av zo1B2pHwnD(^TvV4MZ!}GU75|(&;U%VAZRaxJ7kbYlT6ytayLW|y0(c=t8~EYu^4;8 zCg&b`(=OAXnL=rNcl6>fi8a&j7A*0*Gu$3N}qhhJn4P`Yy|L# z9SB_jPjK32(T_zH2VEUkT!5&VWQ`E*<)9Bxc@Z1=>I^(rwhvM;{EIoZdN$RB-H4{5 zGFVP;GmWNk^0xpua^)xqRqVs+-H3)GDqyW)H%&_Ax4$TriI8G+?oubM{;Pa=~%kvZL0O0?y*r7gD8NLayE!W|GHNtG;NZwd4 zKCnJq?w|1c4k*Ks&K1?2&N$k5Q-FTQZU|DI7%LFFdFcvsm zXCtLOI2!bIlj=Lw$hE-$Fm+s9=ixvTND$NJXP6Bth&z{Fpk73oY8_&V`TiQ zcO6`KAECen@bCZe?~b5;Oinl-A3uD&eD|jxF5ms(yUVAaK3!hk!Rr5tn}BfMU`s^b zk$8Si_e1nuf$J0==MV*RCXq6eHA0m7I%&F5T3I-?xEzDZR;WRW`+(hdA?B=gQ|$tp zEsu8qd=mQXuIWBuG*$V20}$WwuC;z|ZOZeNk9dqkws}mBR1zd+0kh#GdFhSs4MRw@ z;g3pSlKoJ)msVNMp()%2In?~_XYVe5`yYRO`Q>kZarxuFe0%xvFF#)1$tHl$CY)FN zZGpHcpxkeEdi9C&P9$U(A!Jn$?Rgjd=R~W?sN}1dM>O2undo&<&csmaL!e$rvKJo# zNFrqR0$D^eN(0(3H7Vbw(d%=s(tZb9h?R)FjLdor0c?DHueaX=x%p>5)}5I4EOe)o zZb}0u>fKi_m%sbR-(0@_r$1hP`16O$`}d%vI{~<@i*p53{BeN!J^-HRn>_Lp=_H~c zWQ`D|zDed|L07VLdN3Aj5w()zVyG*+nJx~MI0TxmfHnXonJ$Hwh+FMD7&}dHF|WaEkZRfq0^EasRu2_|4_t{>L|$k3W8-j{~0I z*CXM7mjgfNuhQ5B)V#i1BpZkrKxh&o>xtI_h4_QlHFU=AXHPqpW}t^b@~i{ zo@9>)Buk&c(#sfM%JC=e?Dq(J(0%L^UCfPO`#3zX5=1O~Do7y1G`h&+2RI z>|M+5gx!ei04LW%<+RgLt>@72%yXHFtDPN3HuDi7_`~ByTBqp&I`DJY0Q~Un5Ag3L zz()XZV&QiI;Fl)xHv!;I0Bi_&6M%bzAUDoD&z}b5x-H>^IBFv?1r?MCVa_wldAxlG z)Ts%{sPos)ZVKF|+W|4O0idFb%6EvaD8g5PltRRXzzZ%C?$HFNR#7?g%~9=EvBXu3 zL-Dj@#Q`Ds7xeL1fvBjbc()yhC-1ADzo$Qr6mI$BT>v<@^oNA-%c9;{)@L2<&SL=L zcwIrI6T@(o{5cZ^7o-RQq#*2d9(U|O*6ziN&K;knm|ArMKn*Cmuuq5QXlqT3EOTW0 z6!0i4PH*TotCN4N%MNr(rBfJ+!43^DLilGYnG<|B9e9bq@<;FX>3oLY5~1@-b0FOX zD3i`72RT?6LmZO@-eSbb>DZk_GZ#UN5V$g!4sgm&2eS4pF7!zJAf3v=C%rg8Q0RQ>q&*4uB!ikNWfbcm$BTYS%vf0ouH3)BD(us2TtQJ=5W9DW& z%pf>raLx24Wv2x@T+|+{i`8_WF7Hf(%wTOk>_CwMPm%?AwOZhvButqcCe3Els%|;S zJP8$~VOkEyjziozh@G>ehM}xV$dKpoUOIpu>CnQDCks!SEcEH@@p(;}UiKQv-qELF z>apI&t`pW7WOZqimK}HFX$IOc$c}pnx`C#X_RcT0AzcC;OAqkAIn z5OvInrz+XvEo?ix#4ij4&Z_h5BwN_ z?gDV4@T-9|j(;9_{j}>@q*)+P!^COa?@=eOv&?GLCQq)>L~NCK4{!V9ZGITVITgm)1XFqQX|E#`*oe6YGXY4Ol-jsI#6TC~CA?E{ zy)a(ro9Z4e;qh)&ORE|TxA_i$v1EgtrcQAeemCAM-~ETZhTeZAwG4Ze6}{9%jYuP=)_dpZ;kezasWspIh3K*&voyV`ydaGkGL! zUR*8rM=5%=o?4v`s;`z^HB<|2ZUA3nNfKitj5;P zl|lv}ReEhe9_HesYyw1~Jx=+4J77;3jRqpK#)OoQYP2-8JB zVES(@<>u*V8oQk+HUPWMaUk23uUpRb;|-*gLCOeA}bg%y4)kDpnrJT{vF7j${12_n4CW{=JrwKld zZ@L5dJZMb4fj;Ek8M`)TrUCL3a&{nfK+mP5jZ)KCpj(Q3n%Z9jY7%9FoFM?}nEH$%d#NtDolxUSdLF^kWOJ^4GDp%%n4$ls>Ixsl(S`^BrXQgHa zh&G`|W>zGpcy^$iFGWxC2~_}w6|Jc#cL0Xj04Rha%y|3Q9jQWq-3k4?Z>nw8=**uQ z+<{J%mArHQWKnbsa*YYcu%tnWo$#2;JhmR^=<}?GV-gvJ^E!`j zyaV1`C`ujz6<#&P=u{it00^N7wa{bwooGU^1+oQ~vBGvh?J^a-0xQH5(5dQjUK3s$ zjlqV{Fl#wSkKF-0>H6oo)#!QEn0^yUs6F|$yR;KgVFRI6+BW3045;K@Ko${S^z&p)FibJ5nYecbis<;~fLuv*iDFw`; zn;#$}%Z9$s7octRVR*{rBn`(2Xt(>COG3Izp9-NG?xRlay?3BIk2o7-MqEApd`|6~ zNzRr>i{<7pR9B!^Z_t+-&^ss9UQB_jES_QSX&@Vbg1At4=~&%@izVQZB2pq-&*MM` zYJKZY&D^o!Fad35ta&7yRyMuEwQ&O8VLQ#&>j0c>`iFnQKLA|DmDy05uB6H5)xH_k z*z%}QYz>NiaNt*J&)W*q>T$KwC8sKzXE^fo!fyd&1x!Ypgsb|LXB3r2;qF7+l3Ui{ zn-=8N(acXfG=cUy)PYueP=sT}03k!G7(vA6|mW)0=B}eA~0EqjN_4H`Qa- zk+X;%TtgbJ8umrVy&*7gq{W_Rl0|R1YG{W>L+?r+t*@K(ZbaQk8gR?D5C3C8dnCEv9|`hLR6WPKMW2X! zlt(i6yRogBJgyr1ivRjAo<#n^ zJpjvi=231v*j~(%meXN2FAL7_u%JZTi{fSGWYnW^Tpmkx9|aDcjjOaa-2rgI?UCqy zi+)OQKCjsmuOcWhWpJQ0n<}KzN^ybCEiR8y@M=I|w3HK20F?NT1EGW1dzBGTqj`4W zMCmzS$L^(}Grmq1wJ7(KNiD;B)_dpnZt^WoZBUpEz-j^FC&GH!J5bo*6ND2`8)N(u z75&Oc#rer1m|^Lj%mYN8`+!@D;Yc|r@o8lzzyvf!k4|_v;!aZvbV_@6;P?Ohhs)&^ z*7kQ{QqVIz(fSWh^sfTa!XGCM8CEig{&imVqi{6?N%8%pC&|qjf1jwSB3T#EqN8wqq zn2TjUM~~D2+?<8Q{c`zV|Mbn}yKjHEy#MMwlmPGU@!mfg^}L{=R)jb3Rx{H9?TN`` zzC1TG^{t1swDR0>2+|Z08eqq?=;kniN|7ni$3C>y zp2SwI38_#@lr|)wsB$598*s@fXsmgj%)o^kCBs)^8?q}5=%2W~LY(^#fs1>{5|6L& zt^eQs&u=b&`rV%|Uw!oz7|c8I1PnD3 zAXnOUwo*x<;U+nT;9JA zmmFIBVdZ~*RvU$wv+kjm(TsCoV7f%6-XUiKc1P7?xhMO`Ya}o6p7P%_myurV8Sg4w z?#XkNL+La(0H%Rv(B__ic8PZ5YDb!~&gQg!dZyB9g1wwgUIb7YCDw@POIp!50551b zrp`TDrCFIi8op0 zY6*EOCc`enSMyyCcyEqw<6Y%+=I#c-G~5i@{1Z@@i2S#bxXh*UN?|DQmRS3o=CXd& zczH=YZ4_nBlMF*}H=@b|NQg1f<^g4l@%TrScOd-hKYw%i>!0Bp?l0jR?pV;%1s6L^ zE)<5(m^*&{9W-cj9*)sbIgUwH;0r~Z;XnMJAJ3?akNn*~>+#4rDpzgZLC%?D#EN={fOk*9J-_=zA3ec* zj!`|Xa&x7!0Vqyxq3~l3!hC1Y9`)_*t$9@wzOrGYjymGn+?5*I55h#87x18|BdJ}BT%;fMTwn0hVHVv=S@8?Swq4NeT&|x#Wv{XdNIlzQWiR$cG&v28Q8b+w*!>eCLaw z`NLaac*ZZgL7vZe>l==0QN)aBA*`f^c=IR}$=Wap2>Zl1K8iw6L2$7dMi0|DfSPHg zM=Sh0qaf4GeF#Wu+yUT&Hv*6VV$ff{GzoI6FX&!-=)7x~>*}S`DlBB_sS%MHsqm-x z5FMzUQ%b&RylHvq4nUU1%3L%)W{{Crh6^qgrl?kgN^JYwfpsKTnNl^d0Qv>x6cesO zZa82d93lv`5ZHGBD!%yL5%gnUG2B#u3UJYbANsUAto|^@y8w_FJmKPo|Ive#Wx~1- zC`*mn5Z$!OU^80A=_B#7Lg87ZkCKDJv4~4}sELobpohht*L<20v3~U9jRA8YKgQ#j zte-tH&#d3|@Lq+nyPgRXYc>CRiJy_M1GRHR-Dw){vVX+}U}dg}j|gM6PQu%yk!)Aa zcEp=nYjw&L^Z;UPr=GhGx*1aDkUIkdBlT5VGJW)GbkgCX!8Dz_}mr~`&1+m&w9sPxSfh^9jNu&unvKY$2|^Y10WZe!7L5l z4B7+}&?XUI{F|UJ&{*p)P1oEwIENSAsQcmz0)<_P2e0}wH=Q3&so`^gzxr-ugHp(bpHuT8;cIFR5U_6d(zBMexgqnM*Mj_CkT z$i|RJv~<{uXDRq@PPIbgLhi_4v*ndyqe@F1+qaRPNOu9D^S-O|v_FE;$Sa+hySXYKClhM!K*M^){qU6;W zei#@|Gsr!|So;sa$%%ZTqx0sl2$aXr;{tpu1tf(F8!c{d#5(}^=Wpplt57utOx}}6ax^! zvXE~USYJPKl+>d-wrybsW^AKR>v#vwj*j>0{*bT%NGFhAB$8&K5DN(%vjxYI_=w=b zs>(&TDqmq}1ZbEFnnIB|r>+;sQqcrAjcS2R3A8=R@Tf`NVL%*%MC>~Vmv8s0b<<6X zdHLvquB_yD{d%$E{PLdZcfpL|bnP}gb*{i^MS>Ac!^C0a$XMCvWn2OTfw83HU7H4g ztn1HAQ$viE?kbqbQ#=d~d^|djX9vFQ4$vKd;$>ARQj;2{1j;1w5g`LKh6^qYwrGl^ zhj90!6JjbhfUDQzsv%D#0rS|wl52B+J0^5=A^${gfoh~948Ub0peU-M=B|RSY~V$Z zHw9rSgXPTZY(ghS`hn>exI&SNY?u-#N`e)kfQUjvY%KzD1w2REmv$0H;kqdpg14XP zA`d_8sXaUJ<~v}23!w4ZYM9(68N3;|aHE`r8{d3Td4rO}?tWbD2^flNXrXAA7r2vC zlC)nngK$ZXX7cq3G?`q+dr*D;Fhmb;g52StsG(4Vq|=|&!EtP^Q;%^s0}F1L3{Y?) z7;`Zggo)oQo*k1P&j8e{KN8z@JV@gBEti-=@*G&)0Sw=vlLw00Uko=CM)E<7DkBtO z6rq5GW24WWaP7jUaib3Hu!VJ%*7~)3QUrArmd^p4sijMRz>86-EWW&nFTIO~uAQRE^H!hcEdq;iS^Uj|1Tsp-rke z`Yr*X@63eFfMzMS(K}x+;vjVW z#%=})Ga!5!jNB4O0R|=_oAH3%3YGcvn$}m;G=F9A%x;0iUX~18<}Q{%-+P zio+L)Jj_6}B6Q4x(}MVjpsnNbM)&bRcYv(b%9p~~6p1gA62~Bs&SS)d?+T=ifIPhg zLzLgw1vVOD$e((R@ z`w57U(~p&ciiS0&kUg3S^TT%D2S zP{<)N2zz}eK>+NZ75Y+nKMN;14C4TyZQ<&z;~U(eXvC?FC1fO1XtYf)<^z}b{Hyv` z0bte}RWc15I5Dh|03x^nTpB8Q_n-wG+yB5C8T9L%w$Wvgiknnxu(-sSJ^J1)v*L+D zr8WF_85nmd&gB5ytp4NU zy};2?I@hKBvqD#szL_IGf^AgYPVz#~fkYf%%YdHijxRJzDbg{+87vl08N{kO;Fs)9 zeHw5at@_bxt8?st0%I0a$OO2^?-fOy-gD)X_pWsg>wW4HxSz?9k>bDMPAYBHGa2RJ zpif6NBHV9xbj&vNXmm5o(bf#hgl>5pA=u|CsQ7rvZNEsSU8V2UOj+sM{UF1Nu*Txt zhTbgsFq3&afWd9JLQIXUO6zp4Tj_d$_+o>G)ZQ@Xpmr}}&oA*vLR{*M1a{Vg9V7hH z-*6sk@Em(aa$OwYzC{zjtZ%Yfxfl$_GcV3Cuc!R`!)a7CqV-;@NxALt%jfJ$7cg8$ zfZ|fj9blVBZMu5;nibi$kv#7R{={D|g+qr?zgmxz1j^yl#6)mbP)_@KgF&GX_=^;F z&YJqNOVKrK!f(O6ALlrpR69aW%Rs+1k?$52F~Tsv0IAp)zZ65UF5QY&5I*_@24IgQnJoWjXtCtQiX{^lo*dfK5 z(i;m&AvoPN#980 z@{h4(&WwM`hglj2VDCm(m%hS#xeu`cT-I=LJhY*Vir=&*qQOigI<-DiZy^?bX5NYMIKIx++1~Up!h1XVmMMRyDd<`Q2$Mlfl*s&hngg~2Wg%@kIDXsgt!@3!&WgkjJ;dH)$&%jWTcyIfoBUB)Ezcp3O*eXr?v3&4XX;cHVS zum=$qUIBZsU^gZ8h=39IRXt^ht;OX(ws~i0Y)qd|8yL`j{R;o5=v)}+;obev8m0(S zSRa$mxxBA}gb^`8Y_<}Uv!M4`3`ROb4q)JRMMleJ4S?qm3~`9F{4LbS$_QEq#!MFr z#yjpA$^I5d4&q3akp7+AJMkZTS`;+FB7?On2s_k3 z39b~fq!vs3W7W)KuBU0n;zrLr$S-?&fhP>%qTea^LsrA*Dlb$3Z)IN>!7xg*U7EPN z0$qaPDK({!yknkY+cBC+SMxZ8)BUZZao(E2jjEp;RvdL~+z_U^^)mVN^}WaDPZfv* z|Jk#_K`>m)S*6#$1^0H3t@}*YD%1> z&bJeTn0~<{Mj;n|JmzXPlmMbEz6mEzPfDu?I+Agi@;%yif;+4)CnywLuisdz_5=0=l zox+7_gazw9j3-RTT1%*^&|9eP`wq`voK*2GNMA)+d+{GIwS0qmj7Cr#a0A-W1HWH7a zg$rk)G|3#ZQpJvtTNq83+MjIvh+DFvoNjdGC}R@0qJ3sUlw!Z3TZzIMORJzs+xq6k zTIuX(?fk*wjB5vg$tTi~q}2he7&lV;7SIHV!FK96bt1YR5J%S^JQCKxSbF-ASO>w8 zpqL+6kh`R;hO7E60e*c9wxG>m$l`OFD;;2?*JPy`nq-LmgSg06vTzY^Y$bY`Z?|}D zSOA-biM!P=+L%TO04urT1?rABDjq5MWWN*_(ej?8<+RR@pmq<$Z?yi60{I+vOX7g| zwPQ>k1fPh<%Zgv$VH40A)sx!yte4NkMzX(tXPeE*ZIOU{!)5Id>|+}C2Ja^Bd4yqf zsfJ3*0DqVeXNOTPX&YV01Qn%ca@z18vErbZnaRy%|Apj#!^z9C{nDp?EIk8I3;+5* zW_dDLC=>PHZiZl+V+U*N{!Ef$lHLoyQoM>iSFeFDoW(YOAf-Yr6Yo09zOk@93nTxV zI6zfJVy}R`lnAiBa6$he(9qOC?7UZQyGi*gw>*dX7R~+nQ`s>9_RgKE19wR4#60n< z7NyhU)h&P@;L+Dtuo~GuF{4vwhhM(LivlMJtt&uZg{riZy}f%b>mdDRMhvPP{am60 zV8=7YelalsRNK{u#@Z2=q}>W?pRCkBs`WZxB?ub;yAe;V8ChUw&;wmzfgckA=G(Xo zlF)J6o_hGr`e-lk-tF1Ww7qkY1BrFg*q>SekJAQ82X2-)bXHMI-AIi!tM8Ru{aDAL zSjwz;*>Br-)Jx(Ev7K|!`N{MAkGZD+xiB=#!4quOuAbA}Z=5{G!5s=(by3wvD!HH`flnN3`&;QA6-=y!J6Rs8-E-w5St zj9B^CuBdJ6iA)+ZvZMU+Es2$%}v?1E>YOPJZ zqb>a&x^m+b`2B;?V*onG=?Uf{Pc!+y;HH9Y_c$CF)4PQXih}JtGC$jnvl&~fm(rQo zT5eLmJQ~^$($1cz=8Pq@?o*tt?#ynCquq?{u~kdW5^^Am&NiOy6zKH*HWY25|IciEBn!q^VE8kEfL`Hwpx5D(a8EiAQn<=@>1v89un}ju&l!oy=jHj z#J)lB`433~Znx^Ng*cuicGNt7wM)xz-}fA1h7Z!K65_wYAdQa}@o+O)zTtWCYfA(0 zRySUBb5ma^*%6V8pZ&oj=}j=MGVy|7-t%;{rc|bxjT^g@enH2H5zczwiUbx@(B33_RAuU zPM=pvd{$pO8nXFPFnali9rYcPVX1A~faQ-WuqVT;m8>--A8X-Jd_YDLOec_|b^794 z|J0;(S18S0%zk2=2m_RFmycv1Os9?NCzdzt_%k7i*47Cc@=8rOQjetjlgHW6<^=U+ zDN%%idwR+|vZ3)Q(@EU4tbcbWv*3>%?H;AhS!0Lq>s#2$d{C5f<8O*?{=+yD|6iWB z^S@Ojg)J!yI?#z|fqBKcRAF90{Y^4O)8yxitn5?OwH+C*tyil!@MlkzQsnrk-0Eb& z>YyQG_ld=g$_pjM1;(egf!Q0cML3ukbC9?)d-W(191KZY&5j4yfFtWDhllK$3U!nsk-|(tGcm zGx)yVYj>0Mvs^h8Q71p~f6e0o2H1cfV;Mo=JK{JY!@C$%y3IiCBTAp!=6i!0+rSBye>V-xW^7t}zVVzkFS|7KzA66e zPDy61peBvnMudi=7>x)4xO4075~dPlikx0{&pr@E_N(QyopOC5QPb`(KWia&dP1?U z@OqP{kBaPy=;qNlE}sn;2_k{gKUNWj&Ug+fzacb!3|cj2*zzH8 z8>zL4a#}V4iWE*d=PX9ZD$%ceBDWd8$U;tZanL!g?9>$ch0fh1)}5kCZ6(<3xJSyHmcE!h%Z6w6F_>tZ3*=i3+o@^%o)lQD@K87mb`oHy zsfZzXqJmB8G4N3IR1l@SMR|YISI&7SMz{gonpb?D=!awRJ3;M&oEd2PVL44ygU|qM z%v`(2<3rL1PSOuDw3mu4ornq7HMf1L5+3b;N0`5@{5-HBi8?^$5%{Dn>GcoNy}tdU z=}TE}M@Z7X+<7@NP(ZRiK4q5M=-^@q(Hnz$?}e$Dsi{X4ddJ*U+w!$Ed_G|~ z+hiqnJH2sqb0<=`0+T#f*s2>EwW!G#M^Yx#z_d1}usA|J7@S3OTV&$NWlp5ZtfZ}2hJ@1iJ<~Z9%eV z>Tb+Mdt%5`j#)4$CwVXMY;P$Wk%lh2aPtyOZGRYmW^FAguU^h1Ewix7B zxOnl&#ugWbCx#DB#;b^(qr)x7<-0->nHl@MMhS#(9}v2SrK7zsNEimZ+qM=SF{}_! zf3$QP0PI^W*;eO;*Ecd*FEMV2N!(qX&+YU_C)cUT!E~q2vLB9i@IuaH(RYRS=~zm2 zqMV{eFQT3q*zuwufpqBcEYFeZU5qZrjhqNW#%S>XFGiRRKoNjy(Ga z0ydiSp;srN?{h-dG|@h1Jf`~k2lat?h$v6#ZhG4amXAYJ+KJ%|2^g`yk7ueN#Bzaj z+V+aYiwg;}zAqb050`I}p`I63#h^lK^0V)SA)R+S#6PWM-lDzy{d<$$t?7}Rw2mBi zQ@H@0y-osDzIbx-EYKGOU(_cxkdQaBXuyNJk)|h%*f(5>kNbvTP zBIh}|_LV8w^wbDDKt-gT3^gmmolg7=m;O9OH|fxRA5_LgQ_B}FZy+f!kvnWjyVhC$qWO`{<;*lQV z=R0U}+qjf(1ZV~~v!*%@Lb4&5H?ea8htS?7%FR)aFk1)b@!ZSzUl7h>coVIQ#s|w#9|AQ5vgg;n9{_Do+ zlcqWA-3VK(vup>yI#%D_MLW3+^Xm1+!ap(>|Jway9kYf!Og(&GkMq0_kh)9PCWS&f zy_>*8a)Ze#FtHpR3=kHSv`=DfP8rGpjrcQ0NEBd*59L09q)BZ&{&hV3Tl^o<`okV^ z9UkhJ4@x?^XAkg`q(~d^bv2cm_7>GATJw2!&`k^b|Sg}87)A6B2qT_W7)G6ZM z2Ga*0Wmq{F&cB$IF|s8iNG*sR_{_P1{f$J=W~K`QhySybsw2eb0HCAqcend11Qz^- zJY-O~JL~MX?AIrvz;ip~rMu5MspJ{&Q=^C!ADW@;LL(MEQZj&1k{Q7M4U&#yL$3F1 zz339R)23K}`frkF|Aha*5zvZyWf)v2dcL_213j9j{A-i{nKj>;l7eXB*CtBMqDBaT zVJbyNpRLpR=Y3@@h@&Bgj|yz)9vq`=U`V__r=O{Bbx^A46Tsb;&XsH@z>j~D_YU1I zfuUA*{f>!|a*t{Iu@1^rrU|D;;&LWz$wRyP>PsHO`w{@XpN+zqUlT$DQx%9QCL)xTE#sYVB7VD_skTokN72-CSaMuo!AH?=>l zy8q=^iHU>UXQLNkAssmVg;rqcGq$|>4sX=WQ&bSJS!mmqN1;KATPRK&TPRgepPs~~ zT`tJC=Qquk)z*(q_IOVfgP%0pdy}rX9-oUNIrNy$0vrf^P)~e>Kn(j*^vz4a|A49Qs`< zHc7ZTj#TqR<~uw4#Lc^}W(6=UU%n^6SCusNgT%O-jc*tyc#cAG71}05GkLN=j(I8#%Wy=jK?|>-T zbFEtCETk@G){K)y`l5%Jq-jTZ)4aBRc56Nlw$uV@0>1ptUoHXTY=)jx3}7PCfRchT zAH2ate#5{@lt7bgB?xaZ-NE*EtMuPPmLY1!-6|_Xg9ts%qlx~$F`RJoi*3Y41-z!W z`?CNujsRKMBgPU%Ki|mZzQ6pMVXOeD6*6B;?d}+ZiJr+$C};VKgz)g(+z6qdUuC$U z7}wxh-z3ulo@2S-7)Qi=(mjj48(6M?_KtSptzr_TS$}W1BsJ^daJIPjdw)%ynvnN* zG!1UyZQZnTR&OC69+LyWvHGqlN_@mk9nDDs=eyndqrRjlhRsqr{iS${jZuBBBd!A2 z(g%>p*L6JVKVDX22A6%Z9-KuDzI74*74YO?Dainl9t%xnHDgs}m6`bIKQ`EH;=!F| ztsFka!;;BZFE$aryFS-81?3$}lFz=r0e=(2$}7#Kr^!4859Fi>pKX1_331A_D#!be zBeejA8f4;Roje$!-{qW`_{R2AI6D347zKr4#+;>#UpNfIOMJu9sV8PaDVqb0M0ODm1wuxMD1^g+!?p z!>DBrX05Wws?}tovy^Ez$m&@4mFI+XKdan0#XIv9)rfk}{YjAqnD$?EA*~mWL(|1` z?xDVApStun5ozRGcC;m8&l)qWjxY^TH&ck4!sb;xIk_05v45l+- z1VwcGZs#wwg+W%sg)=x}x8Al+Oc|;1H!kc~5Wea|hJ2WK2}l}vjuZFLpIlI`8X+w) z1!Y(Ot^lU62PhCF^|94OpJvbbI_k!2Gy=xOAp8Z)J~hJIGG3fqD9^`tzwu zAjy_fUT=3dDZK-ozy=gB*GbcE-thEo8#0-ztvsk$GACvGTG!I5|iVT7Po;Xn( z&cu_T8;kKY9s|q9yDA7JiTq~NbJ(tfXqPvw6J(MuoC=-C{DVK`p744xImL@V5)4Vq z^vfH}E-jUP10pkr|EBW3B9K9Rf*?qflch6RRIyQ34>O`^TQ^=l>&K|&&_T;8V$%v7gSx!0zXjhW-c0h0D)nBecm|p~j0(d?!Us~bRF)4XD z6vNcV8PdKaX=0(O{DZ3SeJmYgepRw|$_=|i>gD{{ z{=wA=UOi6K^t+z^;@*M*K37BmK@J=#CB-M?xs>10t^g}1u$_bH_X=L zxWT_y2XwqFP|ztWxZCXzZ}Z{|Lug3gfjtO5vDG75+)Ee#eC*6y&iX48O$0NFvD;?a{Ba>qnn{ORi}J zljXriB=uYXFBKdcCF$`a2S2hGMX9PQAK6&962VH);nMr`Zd(X z6h)i7H>M>x!MV&&7yPnH&mf=`>7MO=OQ2SOUj2Jvb##Rd4(Ngvj59GJ+nLr;fZ5+1 zEXMP<@Pt&Jr%1@9y0mtHWFD+l8`J1S)_hsU5u?|5PnBy4wDT)wF5V4SvCODarTY>@ zODS%URP_d?>Suo6*lgAr{0)!vkWlM~?SzDT)%NInx9x&O@#&wJ?6UvwegV)@Ur=Y> zIaZzwcW4L#{Z79)>Ay|?fT0OJwWb<^YRZ$lekTQ8R#h29ue%Jlvx?9Bls-LIl~Z zp%n6f;#L6rkXg)ofy>AAal!|KG?suz6ow?c z-{?$&Qi^wnk8OJQ^#3hweF{{;C=J;z5&;hyQi-G1;u&dV6iFYX`Pp$b?QuqKOeSpP zA*GJ0RwhhDZC^KM@agWD1{j<*W8rKFNw>DnPHC(As}soB7vu5`DTpJ4O7g7VDi|pN zPAB1@oixdFmj0{Ky z*{v9C$sQs_-sIOw3`u@$C1bxvUVJ(;$=zmCmlk3gW-!`tRXh5QbJ@xuq&sDNt;jUo zySXLt+4}s@={)Wd&((S`%nfvw*@MZ^ZjXL(*?!jH{P5Jtmrzmu2_AU8yCP z?uf74+RuaHJhRxAzymP(Tfa)}w`Q0|WhH3phH|CD+Rme_yj*1Yuc^5)fmMffsqO+X z;fAsA*r*C&Hy<>dQ)b&x7VOpq+^M>RE6d9CpY6-~u>ziaMrQS2mjCbrPq%p$d0m5< zrmR4@R`wE+<$HU6y>Hv4!|xvMWkb4y!pxiQY7*T4ClR!XeSd3p?{$OTApa_ymwb`* zfE+dMVB}wPlPrL_Ny2{u<;*V?w1=9ejq6{!B_zeo7RR2_@QBQC^4bRI!@~odZU_kF z{?sjL%D_UM3?u^IexvAOT;JQhO2xyq8 z85{kuS_9zw+7GR$%aeIE(ljNM^OX{9-j9%Ls0Ag zz)`@p{4CNN-qlDP8S+m6lOW$)Jz&#owOVX?g}43IQf=yoA%eXJKGAgkrzQ30XK4Au ztyNF0CG~-Un(ixyz|7%@Mw~!XrFpDxXT6*|)}_-c?>-;X#F(#9F{m{9CEE!+@A8nv zs)_7UB`{dUg*$$&rJ5TIpPe&*E)JUDp$8bubrGykbar?5kp{2J-ipdG^!JcmZS<>} z-92o`{yV&2dbrPYJAW+J5pW`LV&w-pqr3jzf=M-QtU}s@{_HUw>ab_k|`6573^4%Q%SlhbYoZWksE&2IQLUOdpz~dlI&@DSvQK&7ViP85!HZ7b6Cu zwm{1#=K`eXYQ={zoG$cXx=gaqW%{P3Lhe0ouK&|)yZ&6ej#U$2oST3f8@m*g@rEO5 zvx?q?Z6m{4>1a2h{hU%R7CM)w(W*pq$=Q0Y-bG_0Si=e>BJ2uxRLF8+3PJnbY;-UQ zFa_V?>GWOhU^J_S*$b%xX|}2#s9SN)BGU1a&oG3CeFU;h^fu92Ul!+6F7YI#rVlVv zJw@(U24$SVMreXpeXo9RcJ>Xv{Y~^*9u4&6dws8GhYH!XM^L zO}2aC*C&Apd0V%u_!d%J%2n<0u=+GcVySF*CTHzsr`bm zo&>QYe^kGX5Work_cb~5=fmmW)>t{6+jY=EDgPBcO&D@Y=z!&qJ-#Wxk4D)B!1!%{ z6Sc3v7V!91nb%842t0H316$~TUKS)A-cC%0sb-KrS=JVjs`&aUo?Dgd(Qm+>{<=ot zk!-KnKdbfkvfW4JE;dQ$j`tCRoThi-l=gSBDI@g>LH`YGm_Xu)0MDuRxk9p+2FUpj z)H9=4UM!xJQIFz(jt}8Wa;V*@5Moh1%SaYa#)~p1D*MZV4jTL5KePRVB)>maIw_}V z|L8lB+gmyH+XO;yw4l>KSl>QC@jfDnF$M7U8Wd{19m$k==Gcmhu|QFO{1wkzK0xhY zp=@*_ih%G(FV5ZN-xTH6a1yb{@;FWMf&_M2yi)=W?6e+0Q42!SGeASDJ^K?V#_VKM z`}1Qb?4TE=#(CW@J zHv3l8kN_3UcL;G-8o-s}LX|rfIkRaaGnYPR z00l-dm$TQuL~JpJ6D!ET*UIz+U9Yiby1{_AwI7aOb-umc5jeq$Vd_W%B)f0y4Kj)0 ztjEU7uILl$urZw*2Et&ZN85K7l9={R!1vjwSxZZC$6vI*tGCYc606=P_3GnTnpu-^ zYKjIW7XBNlffO)|nhQy{6ijYuK0onaJQ<)Ljnu_H8kR7e*jU3tSPmz6ey4s}D-^2;93Dcv~qmyHuH>p6X``7~-pqF&K;S-7F&ve8#FtddkF>KgtP%p;>EbePM} zetf_bNsNsd=e-e}6e5k8dfNG(T{UHT@v{F?^fnc64pN_d>K0zogSUQ_LPYJcmQqCc zS$YJ41LObQdaY@fVAJvu>V|0T=t4BWsjj#z5*1OXzaDB42Nf}XiJwiQ7@Zb zh9gyT7SXoI8>~XQUK({dT~L}EMfT`3`_0rDE(}_IfOeK<=63G(4ZJTN`?(orLlOOw zT9f(>C0iWhdoAmV6>g!3t}UKcMAN2eRXdGoKdK_Z!0D`HE;xo5H^Q5NW-BX(f!b-R zWp_BYIfxaBW&tD44fk%bRtK3u0g;XjGhcG{Qe#6yw#k0sbf!Rp+mgi~Lju8sDOs_M z1!sZZnW>xwsW#EQH|hM)R-Ht<&LLVBtTNLfF=zrmS%9R7f51#EiP{AntM?}2ETMuy zSh|Gaop}6zH7+E5%sj1gKj|`0cS4Ql`kmZfT{fGH#lAYOCqZMmKZuhl6+J=lmIi|U z4;zq5o$+_I_kA7-tWV_^wpXnD-e251c%dwHloQRKlvCWN+q6FCCY-w3I!(V-1bOY4 zsP#y{*^m5HT3FyBkE9=S*7z2jUOLkE371jE^zJRnzjKfSZjq!Y+F2J(xHZU-_BL$o zrToWL5;P=qI%**T!WV4N6vQy$Aa0vM5}q-W$%T%2yvzPPiuEUYK-}fy376zxK!PHj z(n=X1*6TfDpOBkyzzTcbQB*}XG=_v|JJp_VR3Z1qUhyQ&!&|Ouv(!BTc=VxMLe6Hh zp@10)e2AVxA4svVu{uvjE7C;wWG8vThWE*n*dRDEOczC8{qcR(64I~6kLB*5a#_z0 z4LHBUpF501MO?eRbgQ4-{IxOC<51yNN(_G8O3K!!)Esu{^fW@F$yK;CVL+)YbOzw* zp+@$+vQi%pqRR;L+}2M?_@*gd>C7-#apT8?*J`Ra8>Z!QfLG6v%nzM8j#Qlx-A7O} z%q`Hr*$RifrT`QawXTy225{pi(A;2nq*YvlAh$K)`5k^m}>UBbW$8% zQ_`?e7Z2=ear{oK7c6{Dw2aJ28q%Wfvf=@ybmD_3+zdxOXrs%^Xnf|U3pSi zzp{+LT#ANDEzyxuA;mEp6qsP;3$X@~SOP3_uE;hP)v@grXOlKbCR&nMpI(!4hGYY=qLOPeAn{A2}9Rq=>IZ z&wp?L)bV){%`R97p@7?m)xgW5)rQY9$2vdHhECs`6}g5YfH*_Q)C>$WO%!HaYivLf zpX_tGY?@(3#^*bT4?ByHvV_lPE9=s2&iz<7t^SGN}4c>?U+ zWbd)xabnqMlaOU4ZbU3W;zJ1q{Ms{&w&*-|=U1=_Z|n}n7&wC-qycsSNfeUsV=;*O z3+TcG(5~V0Low3;wz8a|_-SB8`UWJQ8ordJ*Q=WGbvWrapw2yY$7QjEO0U9d zZ$IEkT)T9`nv7Mfn{Dy36L)S;f`F;z&7LY2$yxwuQGA+z@uuIqu)(MGrtO9M-|8sxFcFZXY&*!K$sJIAvLW$(H$ZGUDGE zb?L)zpBxs*cYg~alX$5J;m0JKwE)A!bBoFe1@CgrkV&=I?+AHVpl@J|W~o^xiw))Hqf-e;GlIOK}uY;jc8H{F%cecv}x?W|8M8LA^;C>1pl@ zdc;d(#*dYA0rv1UhyoaHZ63yh5(jN7_BPB?c-*?!eK~M8LHhn;H4-vuxla_zK4c^BTGL|LAmua^Xk&YCy$A~X z&MW1_{oW$ZGdHUdd1nhrD5&pr&$i{-76w@z6FvJOg&AYU(%Tx$*A`gW_IhDhS-P~c zO*~o^RDZfg4SiT2`6o1M`DbvIch+oko0FS1RU{54^vu14AU~GoqTG$MEgfG2AL%}p zaW8#kAOj6su%x@)g=l@#2*uKqqmz&6@ zDd$JRpflli0IMI|u;t>{SJi>d?UmRp!~-VscdA!NPKLQQ6__Hbo2Ul}AZn*WPX8d$q{caanQ&M?g|5lbaiqo>via8V8Lb0QjB_v5FJwewuXZ zs-OM-GXGB0WV`-i^Vw{ZVoFnEX$u*-=Z4vkXEL)1iZYYc_MQ4>Nq@ii8=^zEvrWMR z9JO`<>^0T(IEI^y(LeZ}i0Ifz+PWPnprrZ`PE;T4c3Kqy_W39YE=FEHDCC(@23bep zSh=t3eCokYz&UC5R}!nSRA71|VaN2o`5Oi8AX2}#%UVkF1opQ1*X#0#Q15+Io3Grx zv@z_BhWFqh-s(^c>+&$I;(`UKt>;QPXh0)qgh*vOvpcJ|c82;vlX#8=@GXgWkymG& zca}o5z>CB}cEmbL$__NJNE6D>{hHjy;Z@#uw~|q_suM>WbAE7cE7Ic983tVW=uKdk zH1&wve26G`{9|946S(Yzy_bk?%g)xZ=(d-2Lz97B3a5s}C32p6Hol$OFY6*`+a~nk{@t2`Qqe(PLWsZrNlDp+GbzgGfP2J)8SBorgc2>dq~ibqIhBHzN#uZ z**|-XrkV31$aQVax-8MPe@AOlw`jT)pwjq7yu*Vm62GoF5#O8iTX1p3#7Um}3vf@O zw2A+$NS{|g+b>G20XAAeoQl0#>#EkuM2T#*tvZ)&Lj@+@$lpYd&Z1tu2*78ECX#j*H~HIqr&RnC@#4aaZ|enpWqrQJGL80ya2P~gQusZoc{5WK7w2GM z9)GE-cT5WyEepeav-IxM>&+pHbI`dQQsT)>y=b(BSHWLm7Ehqq$%B zM4uUXOQNQPG8b=4(wyYC+Sg`p`s(uZC;Upnt6SbuBR`A{CSi?gEByGJC-Q7yDNW+Y z*Q;J+rs=+2&P?#B%-%16cWbOONfdOA0Ctw#>VT1aB6(rR9j{Xo`6`DEUKgR6CmxG> zKx+TaU}B$3Qb`~PCJ*nm*=R+m{^VFp&8>C5!=k37)D!$$pDuhwW;<$sCeGQ&YAo>K zLvE=TZ!C45hlh6#QJpm?kop_Ap2lrFXIWrs%kaP9W@sun);>J`N|p=Pof5)m(eq})#+%GFe=?#G5pf;8<)R_9Mz zC}B3=XP#|t>n-s|j3Nv{QjL@`1-!X&Fg z)ENmE;(xrEu5}OSsTazTSQNG-ngN{AbHLbzB9ZT78%T0?=D<9zu-siM<(3OpU8}#` zZFX8*nPM~jR6lSG-SFtS(i3IT?P-%YY8<`E^5mW^zl$0FsjDF_*wfRePtSBN|25Z5?xLXkmKley zqx5r)mqSEUE0-%d6IuO<@YIAY=^aeNxkwEC1FY>!>K-(=R75H>Bfu*%re(rsQL$uF zRzz|m3lV$i8f@(Nq;o0$9gP0XyXCc4#_lZ zV|MM;$!_$l;|)f)n)5wf?^#e|%ig5cCz^&XBqFunL>aw9fy(>F$RziGLw%+&r<%Aq zk1s3uiiaNZ2?t=0h}7 zX#VD#58&}~pII%D(7azysM)>FZskt+o=ed`~=#CAdLB>F_pXfAlAvF;%HN z9pK&`pVL5)&75V#1&2Y!)KsM8$>j%%b3jtWX2G7D3 z3;&BU+zXNFc|T?TZH2^wGdv6_0?H|orAr-jr77G1BSSIMID!|h6WZA*k|!W-ja zP3s+~>G}$i&D+E(e^+-11eXrFh8@omTa#6SLx2M|nO3kynfYN;aW1P-EyhmnB<+$3c$gC;Z#DFLgtPE}qfHyowNlYQcb+vh{FJ)rJmvBS{u>)AwiKg!|4}Ao+N=D^ST&3uQ$Veb=m-q$l@Z5q?!l- zvgA;VX=1gFh4pcNVQ5KM9Y;zq>@C_vUQN0xo%J|}wPTR;>lxK%b2&GVO=14%>} zkYauY39rM!Pp;LvOQ@xos)(SOHF)x#A4`&g@mJ9dYO|dmyCRFkPLJe^OUWTuk>F#S z<55Ff2Jhbur}rPXF0r$f=`2zy$Q zgsShV#YbiSOEQ8X4OYM^g9aJ7+e5$Rlel_8)6YrqtrMqpJF!gX=du^y-p9-xnQ2`e z(A20Ve4%Qs5VSr}Ko%kDls`>wxr`MX{(SSu7_&f+9r`338kP@)-3#6w4y-TA)feGd z_>*DJBFr>IYYv;1T+Eqr$eL@v*VxxVVKkC%RIvR!JGti=<8%nU#hMP5K#0>S%2Bb( zbAXvypp@Y}1}}cu(^#Gj0GgqCnYA$sMLCnLZ6k`U&&Np}DGfVFrv0{O;v2c54Pc2O z>NB(|v>BECp5M_&X9vH3=BT&rJiSapDIBQy6%V1%)YS6D9|QvWY9UjaF$-|uoYV$ zrY8;33Nt`-)-iezCvi6&9_vrxgx3i3Qg=XmagGM{CZ|KSeUYbtb@#qZ@1Or1gOpBn z%U^^5u&gNwRfc1e4Ztj4ssFv43Ywr9Y)x{{wNO26^;y&J^)@ZxIt)RA`1xzLmBA@+GeQXTSuNF6C2qV+-RP9s2WBJt&v_?aDZ9^DnN2 zO5v7GueHbCZ=+x5wt-JO8vpyB9sL4^=OC)fOdNQyqV#Yp#fYT1PNsQSdtl9VXd@Lk`lNh`eFzT)P*ABk=SZ^2_>C9 zg42~HWBD^)H;u?Oy$=X+0tb1^VYFpDvdGlkp0)Sap>fa~`mJNws#KBm=?_Ci)O2WI zC+OZ0S&8@jS38A8<#SF@zkpT|E^H~1k;2B4=9=gM3mUJS8RH1%U}a!(4hGj=B?r^SCMO;pFMQ#vg_}V}k_5l07=C%{CK(CukYtLUzUU-{I^m%mQc5?K~ z*?mevhK!2tD>UIa@X}Et>7;Sst1N8QD=F5eSy>x+^wK47touz{Qcx~y3)=!NZ>Zuk zG(-uR>+5Ihciv7x9Q`Ig<*!9QLW?TxM_K9Nx?NCA;7uc*Nxx6r$E5acs~%)y1VkjH zGt|kwp9iY)4M_;crb98_vk65O++By7EPQ$TNAo5UKW>_!o{p8Hetn*%jO=j~xzBs@ zlRj#qi4pCiv5XvY96J%xD7?OC4bv11=I(osVwex6 zLn$TU{D8`Hr(Y&cInr3_7yat^8D%DoDNnaN^Yib*goo=o_j%WC4Z!E7HiFvB6wU{K zWgYm=hSsWf;U7_O$#`h*q-Sg19!+al<_1N;bjwvLx4L=V+0FEP}-*C03d& zwNr#1A>PjUg~~p;6oK#j$H@dOEkDPrt=V7<(~qUuk?Gq^je%EnZo6G-ZXJ``5Ei|N zQ*EyB4P6}~ssbNnY7`3(e^k3%iGCes-V-OJJ#=@2BHcBlAT6zQqafWmbcck1v?!r;H$$gGDj+p< zcf&B;`M>YI-|qK$&NJuyV(-27T6<*(wCs#HJAyIXBkk14MmaeiGYU5iGtHP!F#PYI z^%?&Vr$@Gr7N~Cdg?Gi;054hO?%t)Pg4k_>E4LEylbeTr2MLrm{?(~UX#J_jG^z1{ zy8^?8+%)NzcX`&uezf3pS4eaOG% zQucT1>$AmFoAJ*WqG>07$-&*9!jnN51!m6G-ZUSjaC5Y^mK(xZ{56fVh0Q=ko<90; zkqq&DYq=et_xPw$ab#+uB+|(Mc{$&YRPo}gja!H<#W#;X#89Qn`USy%b6%1@THgDm zst$iNWBVnEQU6XL@O`{Fch9mt`+;MNu5oj$g@f6~t3{_a!??I0ULX2!)yBSuyqRj9 zeIkz@fwWZhy)mHwX6j zhMB3?J|x+{t?CfMn8XnX$5O?YT%}dZ;Y(Bf>g>iQ->O#f zajL#(VBQ<7Grkrc%94B>xIs238@fyn4q7v$Kz_G*=brQp>{oa%bRvP4lKbc?CxnR@ zpaAY@s}aMmcf>@`4P;|v(!Z)$j7+7zl*4Ggln1;r!^ZB2yC&{c)?`}gNLn39cOtIY zjDQ;P{_P^W!Fvr=(*8_M($I~UD!r0cO5U#1-exFYnP#0yRr%>ptG7+=L%o)}CJyG* zR9G?EYpi7zz~=cLAxHZ8l`@To^bLNGk~O@uX%qy$u$iydg`qqf31kY!LIpDdOUs!IjEiHOA*Pzk_eO<+Nc_ucxU zW)PF)H~STSM8pjmCmrs0zt0sPvgPB?y#G>HY;c5XphoNKS>Y!}Z1|fpsY)|s9(IGw zMSg|^_&=!>>Hk(`N@Ao-n|j$r)eiN^brn$JjFh5{&y* z7MM~Q)Y$2x@Y6Y1!ffhnvlM;x6K6bC6xY1DUPV{vYyA##1S|hTa_xj5!8);?xSPxl zSBFJ_Df=o1>1CbIli@05^Ab_KWb|$H!JFJ-S_|SXnMM^#y7iq{5$ny*`x{#g=_`wO4dTUo152MI`kFT}IhX+ybuz*F$@1lz$}-|^nMhkYuM%4= zj(AKZDR)~;6v{@jbj2AS204*`d!$=MY#|`ttMpgc-#aXK`1YV0b#au(&`KN$?K16a z(7=U3SfBhyS!kFCxe3w{f_z=_LrFKTg}Me_Y25%zV)AbmYqv9C-k|9EJ*NMjTooGs zT1r;Jprfzk{gG|E5aKFG)}rrKm{)1daot#%{%2orix(I;3DU4B*_jBr_Fo$L%Sd!x z7_;%S%nREyS(hEb&*;FDO~LgHfuCE1sQ*6TsMW9E9SLwGruejrVp@Ny&Oa0R@)50g zXH%Th2=b6jnj({xf>LB?=F-A@`$0BZL@?vt=v#v>e)E4Y=Ses)ak%VqcH36; z>B?3*C)lITm@aAf9Ybi`@BOzn=>~ z8%px37kELZo+>{LR`gME8eih5d89KfAHH-t-+7#t#HcbDlZ=a5C`NObM`?WmQS(t; zmP~t{rl(V$F5ho%&TjX59TZ?@XEFnEi_1x0|1L8o*Z13BDMew?BT}WrF9NgP&%6nD zmS@TzOeet5?Htjk_?#e+QRAua66bfpj}{-mY(hjuS9B1rf)(L1=)k|B70iZzKaz%% z!FpK>pB{#2T$$TjS|{>U|1#dz_$lz$I{LSSE&>w@I3b^v>j2v7H95yS&06emYy|43mZ%4QXm7&&&@V?tyu9*#oHp=67b#q zyYC%8wbgY845k`lL9@@rkH09BXwEuu-_~cRfTeYkQ15Y0ltDewF$GXJ_E$dqH~Q4% z#|k7B_l&3k$j4WjyPhqz8|B$t^8FeTz~%hwCv12q*=(Xssq^)h!MsFeD@7uCi$LxR zxr&TZTTU=@C5g?{e@(?(TP)b!yA#M73i1yk4Mf(SLg_UMf;>9Gr+^T21j7{e2?~#& z(LgT;S@O@3?5Zt8+4gH!SX@LT`hN>=VRu2;7^k=+-*Ktj3)m1>dnTn-Cu>7(k>?Ji zLN-0UbkeFh{vqGV(BQWUdyA%Z@|Z@SlTBNRajg5vSH2-vk1nllZ)zay1*v zeL7vjpZh#38-tt0e*Vi}I<}+F<}<;9;nyuJQm0T{4yqIcU5rL`oQ4JoKXV~4i<(In zA3H$n3*DU}n&N;zZl7cgjId|wz6*gC74FN^HMtmyI3I)NM!(f#I0gZ`_e90fc>Y3NZ(Jo zy=5|Ohi`cJZZcFf?qMxcI{$8@09b&$e?XwNY<$}?24wZ*ij|X7-H!r7a)Rc?w?Rb` zFFnm=#Da-+8yCl4oTnsCLIT!cE1E1gLPyup8qR`Eh1jyt_WK70WTiYvs&{e`(Y+F} z0>H|Lb(K0_?p_uyCqJKkBq(lRxlv85`iaYXJjEb{Qd9x=ubC_ zDC6lorhm|>WwO0R(;*lRX2S}*5(jJv2ie-AXehPdd5Q*EX?fQB5bv``8y`lF{YaD| zQet2T6MZj7EGe#v4A5j5a?8VZK;^~zk6@xydBF%QiU|T}Dz>3W@>f2AE45unI3-!J z(*|a6)m>9KZTxKNr|l(*n(EN07~t{}y>Va7(-2mS3Re30zUKn2FUr}Bddcp34H~GY zL5Lnn7Xk;2zKR7qVQlOCEIpS5e{}>1U`(=i#R+<_Gfwfb^+DV?%>f zxC27wxo_CQk6~EWe$fWdBZL&Cu%8bL;)^msiQ*mc-gnE#R2;Lz zW9lOwZzU~}!6O}=7(Mkqf>^CoRvY6x6Pdn8F|M+b_shcp8t=i#SBDrNY08}aJSMz@vM$OU0hxk)SmFWQrbQb?D zK4$ErDhD%$xLz&o3A*jfm_VXpERO&whAsV%q=v_D9{Du&No^F1gFu%<-xLAoyjm3( zaDnGT$RKd97_A%aj2PnQT_sMx+umytgi4oFoUyVZ+w`Qrk_=#~ z?t6Obw^1e3hl@DPR=w6&7`5f&ukHWxqzKg>Q?dxsIaq6PctoZjzx5j+ijz7+&k zF={8isQI%~*7qqi?K*MPB7UJSEeRlR9k+Xy)!wPy~jU|sv0Or?+JH={xf5TCxa zoZk54__!|%;Lvk>3c7HbPcr)rXksz`>gWP*rqD(&arrd$97Hm@TWeGfGzc9<5T1*W0;P#L~3Q=)8Dvb&{ zr&>-%!-muGQAaxgR<=#p9fWSBmSW2pa@y9?)xxp^t%PcM5IpBpMU76XDPrDG;cynO zTD`7+>p{#@v7QXX>9;8z>K-FgPJ?ara=W%o=yD>_pOMTxRbGlW7ws3Y_#Y!Caf^*k z=_1Z)PP3cNFwbq*kNCM8AIby4`1HnQXlFPOTVVR5wFJTdy2Gqr3_f-2rKTHT3^_TbNf;%VvU;C~MRLp%L4qAO+P-O#hQ=}nLTogSL3o~W zhYE6A7|MIeAPtRZ?K7b$&kS2k3zIDgx_9=*tjerL1csOUV-J?zN1|5=hLfU0;%@&T z+_fQCFdtA5qDQFB^YLtOc5weO^?aItw*wCVkC1bV;z!IsZyc2Q`fyC19BaaC0B{$% z_g}Td*@+g~5;)9UG%DCy9UV?5@EII%eNJ`+%Ut+c3?G*#=u9~X`9$z((Az=UL1|vD9iDkHk6l&k(_uK) zp{wKj>XxLhOQ02UxNPd$-T^E zx$tOD>wHfk6~8+|WZ)3(6a9}8fA=gd+zh7X5?E4=FZT_LuAx3{em&-9T0^p2Oktu?+oRb$7} zJyZ7Ki1J%=q9Eokijl}>^bREoor>T^U3S_8lLO0zd#HJOO8|1-lO3!xw2r1)d%Jh0BfgkeQ;t zJ;G~`n%b`>=GbqR+%M87iS&Y=&MjR09<9GOP&(BeG+bdqfZEHNIR{ZJ5i*&JNM``AH)_t z49la#!`;~8-E{Sji)Djv_LiCK66A%tq}B58IVtw%lU%vnABjY;f4G325KxzJA5mPKp?-JtM@LeJ~m+)==< zV{>FLM(&_qg7-#7$P;h8tc-%&n)fU)|7?5o5g2tz`la#ld|$wR(;L$QEj|aduq+km z_IGu?%owKV{K5)D;_63V{~p2T>r6^(rWULB?AtE{mdc-&CHVYVdHwHqyYFLAqk&|S z{5=8QzQQ?B#Gha0BHa<59Yc1MfYYF|n7o3#?#DgHZmRy@X!Kq?0h`p}{jJL#s|vL} zA;6P9c6CUVfe65AYRsQ_(gM;?57)g7X@yB7l~Od}yefLz7NP zk#s|LVj~keGyyaK!u^-59SQ4UnHk95tD>JHA2oz-QbMQ=b&Hqxc73E4Lou_5NGQ8y z3FRLLo3q#&ME1W(YMjPh6hfk3=<2zq3hfIeR$(Xyq z8Z0DfawmlX0%F7aWylN>1OT{;NHR?(`M#8L2um%@qh=TY8#;FW_ z76S!66gbV7u7w=_J*RVFv9(~Cic|oV2@U=Hkl+rrG6mEDIN~WBG292(GGfvBRvDOd z8Xw=R@N>$^3(jU6+p1iB$+`(|HdWOM4O1UaSoya7?RNd!e9NGwkPabzxetKj^!`b# zT012{DeBBPZPuaPYpmRNAR`}3ca<1{k1E*^?=Idhy zw2qznu!s+i!Zp?@OFT}-;nB)X`URcz3w2;$!`&snbNi0wWxk4bbe;s|3eaM$ zy<2uR*tHG9ZSZm5;4uflC^2Erdr^}?8*2=OJR!cWuA*U}7nbJA*~)o!?k1eqEUvv) zq0Og!=nswG{IdcUGaoS&RRYSWjWmi(yJW!6yqT6$UoyhA-aJT5v4gZEPquU6HD z+B)a5H!uL0@S{oI($I6hkl)0nvT!zOEvi%C8hP304W{^H-N#z?59{fmf1A&-Qz7*# zVSg{elRQ%8AR*!LMnAWHO0r*>{}KKBjXcf}zr#pup8up6%@?U)N40G0X+hgGMQqIn z{hp_LMUikYeDenn;3v$#tZbZ|LJ{+P1cP1OFG9)A9D*o|Cf`*PHrK!05{|-YgA{Kz znPaE#WQMa17Ok+;Q{6;$*eU}1kvYd7Adio)FK5dsoowji4ZN_|G>O#cx;pq872rb8 zQy6}V6Lfg!+6~YEp;=%?s-Vr|v`BX1X{-h$y9}Daih)1=Z%9U7^M?@YHsj^iPdp@}bMYBYIz#!fxTPHFQ*)%&YKgyZ>Uue=zlb zE4KWb-dKyXVPCz4&>~z6aSD;3#axom_B97U$Oljv2&MgJ4~H4UPD$ajhhp}d#6h)g zV!j@kb)$qhWB66Ag8eY+{}P1%eP9I|KRe$^hd+5~);Xx_bo>^C$`EwBCZR3%Tg6Qz zj1qTz?IC~K2)fJJ6C5DfjVapMd3JvA1z_f5dy~rB5EJJ0kDK0XX~5JyN%?c4GOIWL z0M;K>laVBT3aY`#L8+G?;*7^B-tNs3?0`zy7_r|b9}+LOG3T6n4adY&asE{_op4#K zeb+4GhR$ilBDScfk{5Sm9A(jdTp3M8>ZfD(?auv0ixgeQBYRMhTmJYSEFjIwbd2+3 z#XTy&dojwsZcTfu%xY8|i7lhG?yfymV}iT>owO)mZfv7! zeN?5X);v6Y;)OPY13+@aA05Qd*2Ok|@M7?)?zXCo@D|)vOK+UKu6;9MpT5-5Fzs~P zWBa_R!#(l5LJ7$C1-Olz^FmnH#1T}EC%gc2chdzQFET@E?1W@NX2NMK##?5zTTjNU z;L^TP9;`2T4bs6IkCs)Yx8P4Uuik%l9IOqr9LHCrlXN@v0{1-jT>LPyOm+#pO`-oN z8#B@YD08E<^!fw@VM8paH*yJN?LOx%Dz;BBh#Hoy3`>RFT5mOpW9LTGN`#M-6MZVj z?$FzM%p>_bcwsrr!x<|4L+v|X@>HiWpe$CXwL%S#Hk?((l$Cpw--8oNu^vmKOokxD zo53FoeCc(|D~u7?xh)0g6swQvj&=#%GXUU9|7DDs*;X-)K41PCAL;+*%GW7h5hA|0`zRMxbB+d!y_*6uPB`Z+zzswdna&8y&Xu%UY*7X>3df2r5 zkW%H;4S;}m3-Efd#iikaSTSXMM@JJ(-4~_+@6x7K?g0hfzW15AY97GNjR*{WfkEmt zBxN9+IpqW2R~t)rvm&9hAu0m>hKh_UN0gsGlKb{pB}ap&_9Q^^55x~k>a=D`7&Q)(c3e9+g}6!Nn03oW`2qgFgvyR zXJ#h{ivhuNe)+y8l$D~^?tZx2BMb2okM1E)rAU)!qF%n*cd_@?Qeis(2Q59%6rV}` z?RsH@Q4Ht+GP*vR-mmb zE0NWE&yqPl^KZFnC8A==s=ejNP;)6N;;ZyOD+*q4( zP*!0WY0QC*N34J)7w;DDRy{xO)Zm!;e4T(=wb}a@Ecf{7o@x1!`e->MNvU}~XmjgY zB8`A|HwA7KiyQ~R9j`vhnKc6;(wz%2yO(?_mno|Zq37N;LEA4+VN|f2P)Q;h%s+=f zISue6hTpLhV>>~p`dy%}FGsERt@``QU*~%;eqO2737v`84l)e7nfXGed|yonr3cRG zjPvJZ_-?(&=ya${Jq>D?2e%XJ4<@Ect=Qu;!zZqssQFZ>osxQSOWi+P7Gol*gGk9s zIokM&*&V~G<9!#Q%+=JV@?Vz~p@zp@e1O!g^WovkvVwq*57F}{orimF2)y~4Q;qfb z?{}0jl>=SQNt_(O&HC|7>KEi>waXLRBU>E4UR2Mcl)YBJ{o^T#>Ar7t=rg=e>kx0= z-o18yIUEJ+a=;8=ZkHhw@PbvJ0D?|kLoSD|_w)NpEL!i+ntazy@lWAKUqg*#MS3Go zWnJ%@98$VNg!7*sM$fH7Wi90BLug8FN10ObbALP#16-Q%8JmC9#-yO2)@|L=g}p$J zp}{VUKGv}1Y^_qs3?)|12P__j_n^K zZd_FCHg&8tvegq=T( z1e|AYtlzX2=ESv*^I7-3RS{q#qVHQ#;3e@u0vnnNiCJCMHo^Fz?2;sD^ZXK=i*Hm# zQn{zGNAIQN=uh+&@lx$haU4E>`nIaF^bK>)ZXYnwaW=BH@8Rp}rY)1pfSr%AGV>ET1dk0Rw+DTvxE5y)(30*e?i%&JDluo_PA7Zri>pSP9zjD&R z7BRx16^EocBde73PXw&m?U=un39ASicGNf3($(v))7vk+?oH}Hg_p1s4#(nApNQk# zOJfF6Af-|G^juy5j>o~@xkj9voC%CL>7edM3;$KKdsM(-1cfi0>)a(9Kb4(cl z4CEr)%ZB+&8=q+A0PR=wkTtkPhmUuFo7dG5rPl4UEVPTiJd-ojdE`AMz-@cozF?Sv zma&hIWK}eWM|(YI!16LQp3A3mKG^~_>quaf;x8}z__$KmtNS{F^Bz9*;Pajqn9~4+ zCXZKE;)lx}bt9LD7B3~*dwTYI%Q8GEm}e;v-u}Tmh&z91l*3`QV|8k|tfQN>AAqdp zegb*c`Of?Fo?n;ocXc0&)KD{8U%La%z1f`10FkH^+A>Xf-TU5B+Kh!fi1pyxV~urd z%c@^#C%rHwnlCfdRsH5)YrV2(b3Vsa1Ljsb{5Xr^G%wy`Y7D%WPA0M-Qi-QjHIk*l zLQxA72~-3b`289i-|(O~&8tktcdH}#QDhb*z@HjSli@~LU<%2|TdH^u-G@&3aqlFI%I)d>j z_YuX;gtTSUj0dsX;?~`EU1)sS8+}R+FYXPWP>Z_H)b5LTb<$2=vUKezmhWpn)j!F` z1d42+RbfRb^x-Nmo1&;RGxxS$WIF}O}dd~UbS+yj!2tdaw+&#*u&g3R>uzytbhO0aJ~RS zau3;|KhEYuQofXoTF{2>-?7tqiO{jZ<{9$6uS>??Y>P`r54R?JwZ&0P$a^hGLPCJES~7=y zyG^UruOItaWNsrBa{sa@KH;L}4BKtuR}$^qwQRfmLD&JEB$yE1_Ke6y8i#HccAOS> zokB(#wUqD{K-<8c*Pm!R&|pqfeEr&J!zB?BJs}>_&jX0=KsC#2^)NV?l^Q+m9uW2U znt7qEgN-%+@-6=6TiL#E5DI*6tUS`}5@mb=s+4c$k-s5lqdink0}_=?4x1D{lVh5u ze=DDZdg17?n$-%X-C3iPJ6mBRbs3 zWHb>%jQuNa`13=8P+EM#xFcwhIbx%;}&4Vc|<3Rryz zh1rRf`JOEMF9j1?Jl2V4xs;?Ot)av!Z)~MR_1t?mFiJ_j@7zRS`J5AC=D+SS?*H6N z2uyoJtbBZ@e~(&BY3zu1{!3eI4ch=PXB@--uCjr*BuZX@IMfXZl7RZOl{oKp7(4VO=CMdSkj|xmP~=e zq(^=9ZFsT{_9%vCkyhg0l{24HDk?>WxoKENkCF?!`cc3`sagtSS{x)(ocB{K%Z}x* zQ1Y%T!Um#i0~E|@vnUPv{{%_Q4mMrUs;rKGO5#~UNWZn2PU44}W{Ukb(i9bs0Xo;B z-9evse$zHYL~L3hp0vBp#MTLfpp$=2C#!Np4u|o8s3({_A^Q<67T%i=tB;hIqfS6z z3guZB!1r8UEM&d&hql@Gmy9hoXq_i9){jBCAG-$>BY9&FF3EZgb+fyaZC4yvlRiQ!7`Z;_9)IK~DIio7{20k?5q<2y`Lo6dA*m>SOxvSlSf(1!tnh09Zb1|s35 zQMCq4@j{y}1o5{;`mTu(;hrbe@@}R16adRYUAaVAz1VRI?Uc?B9rhAMz)wzqK`*C3 zyxCeTZeDXhq7&8}bmz|?9PIQ1x_cyn5%O6pJUC6!7p{#;t*=}4nyxaZ{4GqKvk6;C z`hQx;bYxpwdYv$BS=Q7XI$p4HFn-CP`Lg7~lvB{dWhley?iQB^c9f9l$l(A>Fmtyh z1xAyl5(GmJ`U`P67TEg_l;Igo4{39Ab@dg@)s!VHdpdPX2T7YV?m|Chx!XTV;m-tf zpP}{mUu5~-aDW2+_}2<2*FQwNg@RX8`j!VuImLWt)Vl#pbcK>ag|3`dv`_A=I{DK& zNX7I;i?CF#v0`d6xg?iDOy_s3-kKo0=1I!KAQlb+g@+lgHu`DiWeg9-7O6a;@~_s? zXhLt7som&!6GEpc0mtr%=mTU}O<`$QsWJVT7M7WW{R1}ymc*O<%<@@lq>^qMI!_wO z&U8Gc_-Rp*3fZ|5gT|<7%WQrzy1$U*X(WIGZe&a&jOyFy;}XR(FhyM+WQ7zpUL3T_ z1{5YN%XXsq1k!`W5GS*7%euxA>7|mJa(O{f}X3CkE$)jwm;^&@s1wT0y~k#9Cd_Li;mG^n7?a> z>XpMXv$%(w^uOgk{^>}k^T&bRHT!94Y~-?dDWhf{zcNx1oqj&N4T4{t}ZT{+{pL(WAW!JC};A zmBa~}(;-%RW(~Et3NMl{c!K)az!VE#ELn;pUvc&AT%h~y+-8!>UN(@1j;i!}NJE_m zEZGeTJ%6_Awz^+-y`uJRc)sY7laHq%FB34xm}2abobDxaGsH zMav`Fuu2)o=lMxW=#C+>^|+UiDlI}zk|-_jz7HoaEg)Z zWNk&o!BhokEF=){AxZXqIEyokP3E%Yy7%0nE+Z2?WX9J`O(Rvw;PQ>%j4?iGI!IN- z0<)Uxr?i1X6q(uKfD21^NvHQp5Xd;|mo~ip({cK*_%8mayB9H|J?%}r8Kfb!pwRnq z*9jnDO6ZX^vcei`GX+FetrrlV4S`xNo72-VOwo^eBf=@%9?=SDoasz$S(rcR{kMr2 z7_nNZf~@G`IJXtJ`|=83%Gd$3s5>d1v}|zWgP3Kozu!`U7=4E{J1bWSK{+gHe_kYzh=HTbT}tvRX%F6b-04=81XDPsMk z2m9W)LLx$3Gze3YLyrz#-mGFuLVHb~n?oyVJv#tMxx1g}K(xl{UD9kz{)7tXp&kn+ zFF#IfPSq-8cMSgfA15_nlP#9u8x_fnt;I(s1*506!8ZsU?Fq%3)TMp=&8Lsyzu^qz z6XtJg^1D$XIZm>foIj(8cB{Mkr`HfEL9#lIA81S3zD&9KLhQa_ewqz>H&(?tB3O{L z?m|Dm=;b=%0i?(F{I?LMEWBM$2Rel7t+ z4xLgGYEFY6CaXoW1oHB?eWS2rgURs#KoUM#@N|Ed_5QLNxA_A{&YqZ~5i_sJ_KyFg zItTdh(^5}bbJP%EcJ*LLHA7TC2!v@rfkXVC9`bNa?d_-UfwUcEJs>kwL*E$CmG|Tu9#AJb&6~e7s}v4c~LUe^Ecn# zRjk21sdGK!tPPbjg>%N`R+D6p#Y zfJlECX75QZh<}#og5MUZQfMQ`lq@?u#1^2Y`MFftB%hx$cw; z@YC<&#QK>cEQB$uZ1Je9^t2Au!T+C|M3-wjq5S=7Y_B&Z;V_9gtytw@uz&v*+6pc` z_}&?)-IaebVINagR<`DSX<&qO|7a!OYbNnH@dezKyIuHB;`lus(I;$gR6PccKezhY zIpA9067?jJVZA`s?h3_N?2GU>5ng0e(MO%N0A`ECIH#yjx;DBk%H|iE!J(N2Q7oErPnlzA)X&pX8mRyZ3(1zhfK` zPt**%!3BKX9z7w$89_RXTyJit6)Z+4Z!)6&j6!*&D=RQb{PKU+aBvW(<)TCXMA_K9 znR1r;mM|^E)<6sB-?ARn3a^ZqU`aR1tHA^q$K)EDv|=wuWDQ?sUL%&pLLjcyXh`FZ z{a`Uys1{`zBS`W+Jd5w~qKmL!>RBjsM0-5;{p~$Pb&~Z)Rhk9-dOK@vnfeJ7_*mll z)JF+xK|SljXeBCY-zOtNpHUQ15ED?!lJXZ)(s!84k1ZADLGE! zTn4O1Jz#@Vuv{~xF+z#=msa@^TV=8rqplGS81T)=Wx>Wi|Z2XM`|}S)e^mc_N5BBsmtV>PM>VVMY2Xr1ZiAp z*;{_Q{E#m+A4C^ZKlITImH?g8OQv+wiCj%Ju~2nC(fGTClm(1@vSV&d(oIcGiQ8;I z!7DSO%3S{>=6&IX`pBCCi6$DMJRNUxkf4K0uIYIW`W}X+mj-D

+KwO|?0#bHFg=Pw`g1CV?k*}uGs=%X9+*rEepsbh-{9{U5 zRTjz$9abVc(=9A3cFFrnUHiMf zwXaYAhiqJd4-?qsgP;FvH~4sqYv4^4KdEbrCpeSd3(Qdn6p1c|-CN7{cx;b(IY>&p zRDs?qyf%q80zM^cu5eV zP3I$w6&GZTnyLIsnm06-=PJkJkNRuijA)rM0?FD5JP$4`?wQ6M5#e$hH=Er zzzaFd>807ES)B1*cHcmDZ&iLia&}HDUHSzx*R5VT>7DJbd%eot3HStLAd!+_xd+-2 z%0^uM0^Ooj$v>LfkA0AILQmcO7Og;V`H(nrDTScV(nIvn_Bk?@~~))|~0#a&DcE z76uihPv05a!9K=c2Q9~ZIlusbB?`u6|9TG1Z^cL&RQ9V_>X58TMY;?%6wrixW=+L` zrWQI=bDTRR!g$Y&vjLjHY}U?i$0*$gdfjX{ik+M5S)5#-?m;lyyIEhJk;?S>+^ZNCc^kK>KPl5u!X0E z(3Y;tlSV15>Ab(*WTZ>sNa-^|FKBxXGwVDK4^@$qmEosb{ELz-T8}rlG`fomA=QGP z;`53|SGC`0+|jhn#=Y_&iKfn0bs?KNp+Js()!hD5)p?@CH_S znp5XENg5uqd^;c;5z>3F_OaI(qlPBNluSK5vv-MxY1?vVe~TNbz8~t8Ki5eARC^Nk z@fZlBd~yr%KkLoaySrHw8XvuQn+NX|?S1z14LD|V&~U5p-SrcL?S^be(Gso%ot+ls z@jhpr+e%BedM0WU=0Qx9CA#^CM=N-}r5j#G=;{@v+rHS=|CqyV^A__5NlmR#R9wB< z&eZ8ap86fP=7!_Nqq|^viVf)v*7JIHyEhJ$9cQn!Dqf zQr>We2!rF@C>|ZypB{MO9wMgVx$sMW!c!!mP^9qCdW$@d>4JOl<{ODJa!@|Fvf;)w=fRIuk zg!F0W%qKKp?Y9$a>*X`88zC&Py3=~Miz;Z5GnB5s`BrA!6;(H8i9rMwCuDBV0);pm=l0E9bS#;vJ7UbtEqCd^+8MMzp?qIki#eiDPLh zQg#UV)&rXh8U8gQ{CZFRP4gUK{pZwOR!SU+*O{yUYiVL^CzWCG=^y&h&F4?nGp!vP zBNp7{8y|m~fHP*ss`|bfIQ2LK8NkSlKQ{z^j3j8eYH971wK&W!rbve#5{B%F)cF5q z)`AnoUdAsICpc5i*Bw7yYN$X@F*~1gw#9lyAHr7hM=c~}sgsgZ;CkqT!>G=e0@Iak z8$*@eaX$hiEbo8G@8y38VEQj07a7~Fq|KPRE%kN-k~g4A8w^INxY{I4Ia zUULNW2y&5gxV`f|@fE&efg_daI&ZL6f38K4u9gwCQ%@csv8sM2t~&wJ!ECBOPN)sy z7S)05VmQa*7I?IJF!mQDor)DGzc0AKx;QD}mpF1i6|igV#-a!;i3aUJb_kD6+$T~Fw zbo!Yv@VQ^a5QrkhHJlu-cSGkLNFiOiSYP9Fi+#hkgUf1UjT@u{!z!Twl_Ouie>(ox z-P(@!-&hmhd@9d4PbpVn^m5%ez_AN6%5bJuUSE}}vFbO=*Br0Eo;2iBXY{pwFLu&I z!W`#s1^2MT$3qyGw>jJ6izHr5y*BE8MT=$>{_YW~rbN|vZ(nJuRs$J-?^`3k>SURR z@uduTkKQ3C#b+O%Ovri$Nk5zLg`8-w#ioOx%rM`zvmRQP`@^ch`O}%&^Sv*>Yv6w` z)~B7%WzK?1PnTD6m;LttTB8$sNJi;Zbh3iYba#z@9LT!pPl~G9a}_G$sD<AX29Cn1c)X!bKEnzfu=O}q)SvR`~y#6qX2hQDp<5b7t0Dc+2r*U z;pQcmn)ijh$PgV;gLtTkSRhr-p3`v^Y>m(OI>@SODM#FX*-$_m(n}mJq9XSCI+vKA z_`|Y-D9J@Tv8oG+wKQ$Im_aR4Rjc$y0hNlwLy{2%8M!$c$!sgHivN-`vbOuNB-fa< z`D-w~J6#I4J~gP!y;1qF0TP#P7b!>%wRf+FpXb*6dzLxE(}LHwAG+L{ECnq+mS6J>EEa1^geT&N`}%FM9B}yBDWuaVri%THI-Y;#Q!zI|M7P zrMMT0YjF)hi$j1F+XWYrqhuu?)5Z!eTDb?Kw+u5X4D;>&~J3bAb;DDQ{E3rLF3*c&`dV z*v<#oBM)}{y=e{o$opLqt5h=UNZeB}6>C&ceq9nREf-CTX_(CLzoajWP$Oc5Qo{c~ zO34mbY46?pZ&&k3p(k#4Td&=Sj5@qcs%1=&ntjGJj_!#HchAnwt}%j!f%}Z=&(>M@ z=I-Yj&yAi<9ILp;&{s);-!>M3FZR!q$q5&6=Up4}8zY3tcd+PDuM5apd*>B&^uiu3 znPM2Uhhz{|GO7zAp;D9fHULWzrK$0GYLfr8$Oeg`#=8V| z)@#N0m0eYvH=_(3LNWziM{SM^K?p7od5?KS1HIAuOpVz1f^ZfRiS-ewz2$#Ipaky! zp9thZ|HXZ3WdBa1&a_8x`5y#q{8EUhX?g)|5{|*sXb4YdBw=l!ms7u9keAk_aa-lx zbx2$?*)VR{eS8?<^KC8PtHioz(X`P6$;|je$BSfqhSe}k93G1M3#5?9jRIC}nU}g= zaU;>lKJ1}=-uxQ651Z*nSVnHxX%fX9*Ij8a*kIz$ikxYy`q2X(?_q((7judg8Bt;_Y>ZL4_xjO&?XF%xY~z*CMucieNKV zQuF}QN*J3jDf)9Kf*l=R_u$Hk9S-nQKuulm72)3P)qxT@nu;KN$nE17za$7eaJ%?o z-z_UNcWV&7!xE+5gM@_LtnyCoLz5dsQOW|sCvGbAnb5Xg8T(^q@lEpFW*;2Pam)|u#=LNXJr=2 zl|tesEAop6G~^aw@|p_vyKm)lH4t}$tv6COC^z|h!&0{p(I>;(Yj@1a>-csP`8fk_ z9&u3)JWsYg_uokskRrxlgWd7km6UG^!Pxm=g~w~RWn%F!UjM9@r~oHYON5WV7)gK! z0B#UL9Xkk>mjixeB@?i=4nyNbQ!FVdNn)$TyD7e?YTqE zkoNsorO=4Kq%JyME7$IGuiP?`wQz zZ#58OUP@o=_KNM*-F!MJH+^fO43PN*gc%OZKmMcNjmg=*%gUEy4S;|0xUy0Dphf)=MZ+29498!e3`W;b_MG||eI~e&r zOdG07$(l}8awpv$l+ToEq(e_KmqoQMN5$KT$RWHLveu?gyM5B7*HOZ(7@(_6kof(P z%%~$CjV@_^il45Oauv~TT&5Ot{bz^gr4!JVaQ*PSKeusv=FsXvC+i+aY#Im;XYI4J>=5w85Xm#Xx za?(Wt@6TIgG6G0V!jVKF1Cm@E;nJKFqNgSOHu%^W5_ctTssVeR=Bw}B znlD-8+ly~gw=034KnJiCQ)LI=?g% ztNf2{I#rja`mx;$Wucv-j!k`f`lV4;2bV*hGQ=D-?@g??3ynlce&6@HH4VHeK%n}5 zMT&%ZYbl>04j<)^$z(We7)hN)cv)H7`ZY_5)e^TkYA=O?%vkCz;Ifg{$jg$fy2tF? zO)sToI%Sr~$UpIqtsW#NCw3Y;CQX{#&u9>v2L62JWhX*2Fnz1|6ex7B7F05m|IgP2 zEcyU+lqrN?dbz?gL+%VhjZ(eqxF zbCne;$+`L_G?=~>{dte*jIy~|F*I_k1V7DFtZP$Py_aZTncbYUJVEuwKV7Ht&&YS? zB*4hKWb?Z#Q~;WdK;B?j!b-TOXK%(9OI;m{+q*crE9Kmv2W}Je&;7Vz}=bXyIZAtX+$DO zh@J0sXIjaSC=zV*eHZV$%kBYH`G`m4cb*`4)`xgip0qIiv5_!STH=5G zVaNJx@lz_pJ@nzN-$m&DNETOTw0AOs z6w|v>k~dC>FDV&v!O|re+FCBt-hBy8oEhjUnFR!TH9uD^dp)+KEgw{CCS{ll6!L-4 zcu8(8%hwAv%g;9Y2F6}SaM8PULzyv>-?rmNc{R_8lwQ3;8YtB)I9&Q`7_VpDAQt@N zm%y9k_+lM@IZqoZ#`$g)!qty>{M4{kshZmq;a!58r*v&$#T~0DmPaX-=$IlbXufrB>1G=4*X38lp*K`S|3?9SUmLHmVFYHXV3#U$Ff$%e z@PuZB>fJxj!V_D5=5DY<2RO1WF7EE2Q5Z59`3&2b57E#m7}5GE&;{uG7!5Tr0$>nY zz+aZnuC3FC4$pg`jg&GkN1`C=5NlRSG00}yo%^fx-6t8RlbeD7l8xa`eI^`(QcE8n zUrfdO`%Q!H{xw!ZDcGLC*#-kHJoZrf{F7PNdAcwC(m&86bY}BlzP$X(pkOM~9A{)& zp=oWDi(-35!;gQadUKi*%o9FCj`b{vSC{YfTPa+t{DW(P$&N5vUZ@vQf4cZhtm@Zx z*COF;!0TT~KG4RptZh0mdjXc%P0#>f_qV8delyw4>|RcYEkiWZ0xTG@cZogW*u z@{M`WMgu8kNqcWDr#X>>ydK{80f0zAZ95!DNYuaUD@rZGWV29nb!kHLl_zc!+8~VH zzB{Fna$lJjOunGatNPF8OWSYzZIWFJpn!rm>V#3_6g<#)PBNGOOY3KA9uVz~C3dqfP#p1Ml% zNOe=Rhln@tJ{zjoYG@#FBIZ~~$kAVr(EhswaZn=;BqY=#WF%C?i2UDYMJWIGEDBN) z>i?VncOmY5%h&%)m-0hC=?Sc<8Fgn6slV#JmqHV|sTmEz2}oSxPflSUQWNb@E&H0v8j zcI!mq@5GE%)=U$Rjyr!dp|q;Ay0faYqmjEwQO~{AK#aT9`p%=fr;pw@qO%aGsl1DW z%hpx`?#%H<(!xIC(;+`8Gka{%^`Vye@4S zDe8z779wY>uzpXLwa5!fQvN)utiwT1t%2&gADN?@WK(+1x2;#@FafwVuwK3QG#Bh;Oq9y(BY`T{`gF0S<^Bu z0`4>3wAN$i3hBOhzTpF&{#*=!2fBPg=Vmb0R7_5SVv_PSJZ#SxYE@yIJW6<5++UUG zJ$4bEhy<@fl^T|(w_nLT(M;1z-8&P?2%9B9LV`r#t%pv}3y4RJGy6z61SR*e+SkYT zmVJ9vX0=!m!6IumkjGYe>b;T%?HVn&&X~aA=Lx6ak zq?bc^m~r;6l=|$mCRD^|m*_6V(XjXC3I^?|{Uq71xpynx24so;2rns)NL2WNRfLCM zUROA`SZtuG-?FGekZ$PttAk%?BuBHnL_o@Hb7k6A=t>MdiC8KIEYeGxKQ+zREEOIf zUUKMln?8Cr#W1xWKb^R5S4cPQ0}z!oemmUOwLpS&Lpm zOl%3{3M4$IVP3jdN;|X>4vUARZIiBUT(Fq`9qoNb%J=oO8q04`a*6-Wv!wMAc+6)T z`t*WC(yAwC|I;7yRVz|e+K`@*xk4#9z8>qDyM^!X{SDd%Nhf>7*#lpW35A|bTy4i| zepkP42dX9L^kdqJ34Kdc73uFWuWz&;=}`CuY;~J=v9vU$uCA}YI|YjLaTRORm@uf{ zvdf zu-yI4;mfCUqe^{(;Hfvno8{D?Cpnqo7ue`+DOvv>aBr~tZ>%U>RZo~2APeX=!hn6MPaZ%qxY z#PMqxMvg1h-szuR zD`dj#ri&>jJK?o|bcq}D(?g@92RjD`drrVRNFPl(NW4H3=ougZ1C5;fZG18#t`_uuf-h{Qb1jyo9Uk1@`*{U?X{@3O*aZ4ED z7$O1(_?An;N3OKs=Af5OhG+1<58P&WQpl933@~W)%eK%EZ*l-;XQ5K(ljFg1lA-z zeA`$wbN1q%knwhUV=@}}n7H42dHfvlb)3C0FR>uNO90jmD?Zjt>xu_Is;&-^y5v*m z1>S>6gBaEk!ds~o?69A@2)IN9cb^r;hn5NZ9Q9qnX%|!Zk|ibE%-_FW5f!ZE?%%FK z3PHXU^8VV|Cbh?N3CHOQ-=3*ed_A%0Iqt1Cn5s65dI^v@56I-@!o&LJJkh)wkR~d? zg+^vVD@(d8S5(f@VT;z#zg+0FwodGlJ9~w5bD!%zSPZHNW0%vfm0o95CQ#T_6i2ub zP!AgEq7IW#?1dxcTGOZ51qKF|9Kyf{cxInX3nt+fQn}BrNH2x9qM&=nZNQ_FmJTgP z+6*bQ=y?-bo5M{yt&DmaE-)Q(tU*^G!nz9lfeUG!d$fM=`d_|N7AQOGzq3GIKZPY$JvSBqfBi`EdNggxny|>HRCw9xo>laK#Yqm)GWD4oeqD9am4Z?U7d2^~`@l|wNf zaH=A=%g-Ixp50020}Snc;HSQJU`TK&X&zXAikv7)- zQ^RO;2O09vJWKl;o&LW4F`qj(r(a|zcBLJk)wwQegg(&evN1Ur@7C)n0Y04R(j6eU@6C<+EV+sdRp(?HbJ%&{o z20<{I1v=M#whX#-lt^dd7D4q9zlycU0ZzX9WMd73l3($zTqE-Px0-Klje-?zZ4-L9 zhWRb+1w`=7XbjZ}2pxL!eB?xAl`wK&#FZpxZ(|N!e2;8wgPKn~Yzh>&LnDuq| z;qmUi9^iX@+H`op*H3x=e7^|>x6p{XCTSvKdiURuR)3uzyS9Guy^!`^)8jfi2A`}P zL5BmQnD74Z->`HKrY`72{w^wDPH(0kNhjf4x+Mw4Ut%oUp7$2`-`_fN$0^Q#?H%*V zgtfRhhACUVy-EX(N)nDvo_;Q?bs38JOiPuYF?#?x_TRma$oH;xcD@o_0nT0l`&0OS z40i^aS|dO^xG7;2F7mxPvea$8M7G)I0?aRorhw$b198hW_X4MF5CILmZuy?@mawiT z=;ibCt#nW}Lr4<~92<5cd|V*DxU=PCwcLWPN^ooU>xx6ENQrP}I8afTr$)7oiiE-Euit_5cxjda7d9?xGQ#yM? zjaIRV4|>9qn4}I<9AP&uD%~>Sl<01+J^jcnT76H+?~CQyiL%z4C{YHyKAzv``(5nP zEzM4rKf1nb)>z^xowDWuHeAaz`tbZlcT7AbAMBrL+WpA|o zE3%5K;K+ODDF;mpPf#3e8aY}t%53_|^gk)NZSh^}`+-l=5~2TH1+0iADcn+LeN`{a z#2YoAo1eBXc1rN3hK-qjc3gyp+=F#BuMDF`qzdP^ZRl=jn$;m6+D2^{4fOgv({8*( zD*&YrH786qs1z5yr}zEPKpc)g9e5Qj(-6?)c}dPpkCAw)ApInbk+Ast z=1roU%B$?IcAnVf)Y?gzq+y?oK~yRBHt8tI85dACpEX^gUHpTG|Nd^~)L!|>qHWU} zNSJ{Rp|ab79)-r)0iNV{1gi>ed|wQD4SzpA_%-fD&;X`JL94*)t;6Jm{c(`Yzh(a? z4ZmcD-V91uDLrgd_2nmQp8D@8OSeTWUEOL+8#W1{r>f3$n$T#n5vdaySc+kHMr+w&= zeTdI(eYs?lITi397hE8Ymm!)j>WM!{W`p->c5tn8aHT7e%?SqEh2@%yh1_`v!ujs* zZehY941geOhLCnbKD;^c>Zvjnk^CPTs&{zyJUsF5N|`+FUe z$fyY7-wd`MxXv|E52-c9k$FbjLs)CQ?*NeOLmFe}x;FhU&7u#(wM0Lgv+% zi8WE4cHwp2YtZK!#{(a-5{575M>7PGSl_qyy@%7FI-B+_yKE+kz#;ILdb7Z*h&y=y z^5LP@fbVUx%X)7t4d~GWc!zDLRmpSmJ0o+hE1fQeU?n&|ef{ICK2kcr?T2HhbvMIF zmAXMD&!3y$41EZ(ZiM7)1!sPvX-DE32?(PoZt@Qj`b6u@a9$hE>GWX-N0A00_lI#c z6l+$`Cbd<*-n5U&@(7O6Rz4^*W>s%UPo9?_4|U28XTxw9ZJ6G0Mh5N$;}pJ%OeUBL zxi~g|I5G!9A^!jHp-*l3A1^Z`=QD@ey7!1fd;!Ai-8;bJadyZ`R3Cj zB@{md_r7Op=kCEh`&ez#E~#AWmTap}Es%$<2Yr&f-2yoTgZ+av)OGdFnsc=orItcMH8_i=mHH?nox8sOlQwSmP|)q|;xqniC+@w2Y;das?$n4%E^6+6@kr&es55|I9S&w}O(wM)x4j9!Hh#GNe5;O>Czcv^};w3w~58#!5fZ#l~(Z zHh$RFZ(*SqL=Q)8#Du{RozTSI&S$TMp3=V76iyhwwj$>JmyDHpq;pscI?JRad)uHP zolA;}$9|8EeYvL>=+!n^h%J~lczMHNG4x(yE!lMFGTmpYR<%+ySRCyG-ZxzfzHn)T zAq;}2H#|)MZ*C1-U)T+Lj}xb7=+lW>=X45oGsy5Yh9oA{5P@!wC)Bi|lXC=54|03s z8e|$=5BgWyS zRDAH&Gh_1?$300@883klHAaA>!2A`BY=rx^D6s9}V4NNJYAPD!9^ljJxql!MBq0<$ z3i=m0w+jG`njK7}o9XxS=+)PqB8dmlDOQ(TM3pTvgq0N6v)lP~i%RKEX7(f1OEkGu z)42aA=If^+=yP)j#vo*jx*~}cfbJ!$7+;Dw!!#>?h|V>I^kk}f45c@J6w?hah)>nU z!4+(JT{`|jS$#bho7KH^oR`sRYDabD2uoh@iUIOIG_}#U&4FZjyflL zN@cz5H>59Gdm1Nh*y1<+bu%Tr!&7LC82`P-keup?k2yzNe9?U3<$DD={s^*^dcL8u zfP>}BNrrNrY}NWnz~A|E8seC!^Z=(q@g))tOuET#D=G%#;zxL1$FWS;9~C~JEF z_zI;f$Py^=5RLg1$Z)^12&xOoHGg7pW8!oDg1NAoNq>3jcw{@vth9o89r~xrh}gVP z4b7wSCI`pt*6F);fJ_YA8&XUa4#UnE9kDnyax1YOMNEtKB2wc-N+eaFw#r^~WTH4W z`$KnYXU1-4!ZX$|GUZV5n-kZKp>JBrXRWKJ+X;Kg3yvh>o^e*DAMBp*as6lfBD zDX3lO{wnQf=>`RT<@&-lg=Sdh2YcySCOUCvPm!DD&)jqWK9Fu7@3W%{WBZ^UioZ>i z@#df%BLCG`By@Enj#Pq1=K|iFj{ZawDQNVq5E*-|h_!?E!(yXJ`Nv4>Kgd)B4_`u} zhCHa~qYo3It;uFW?Ru&U11btD%aMY8x__nQfgBuftiH?sA$BP}l$OaOl19T1`Vp`{ zeA6_#bu!r$!3BT5HV-+R-q=eH%tdkmGhRF5#uaC()&YF4m=URaAkTn+UkJ=x&U2i3 z;7rI+tA&pd_CK>ieNQEZT&t(fo9US;o3HM=!;?(!|6Sa4Pz>TSy)C?~`8a*JByWzV z`@rGk&CK2?cxJ=yfsih?esxu*WS(i597_rF)xJBEU7xax?7dBPUd**Lw?)ls#3OBT zGF&+!N=-eKLs|Aux@yI>{LT6~^575qN_S2qA z>{A{7RM~e2<73YGRe2_f@BRDRVzn;C((kkDC&vyG|zB4 zBs2DQ6!n&zF#Mhmq-f=QyzOK9s9?DrZcZs;iim5k3rklP)`9ibP>`JDax{q5EhuQe z{1yWH1L==P@=IBY1MuY4OJ$D*(7Iej2ZwaG!=6v!UcRkYg^*5vRM=TR2`EHojvZs! zW5>hJk_OvJkLNhQp;II`Cq%D+?T-VYP0H(21%-7CWkYGqB(X2+eJRN%#HrwfS6^#g zc33or^Mz+Nuc@=Kgx-x5X0{vyl;$Kgp(chIW*aO_heQ%GJBg**SJCJr+q>X&$F%LHCb$zk z)fL8P0U2O#Ra?4?G?6!JVm80YAuW-DJSUq=!E(F0x&SvVlpuj9=lvBm zgL#p;6z4x3`Pp+C;)Ht~X*u7NWJw+Ah`1=hO$2XXD>t16tbxwfAU$rd_5fQcr1*=D9 zOC`1#1m5ypDg3bFHE14C=B=-(VGb~u*bm1`+q03gy>zJ1^1FV1O_BOab~q8Q+do*+ z3vQr{QjM=WUuC80-oG50*yZ+zZ{xi(p3Pm*Z<o%YD#|^Fg zyZ5raE2Yntbo}nC2m8Ke?JuJ*FA>ST_pgI$`GSh&^M0CH7!GH3!7lvw&P2Nc08fW( zH5x6$`xlD3s!b*3k>{2Bfh7f*bUG8>HBMy1?K=+9g=La-^1#D`MG?nc~T$A>auRuR5< zLdMWw?8(cgl?%V&TYvVy$9aD>0a$--{JJahBhN&p(Bc$hPr829$78OrZltBn)*9O$ z|DYYwPbL+h^m4d?A7<=aaH;Vz-lD0z=_#jMjbzD(vUTaPOkpV9L#bUpB2RW zcll&5`DQ7eMaV$1RQE>-2@56tGJea!h|B_UN|=b1?mEAcw+1$wgKj=D@+QBM7qeLh z_N!RpmirV@GJ)UN!1l9)W4?mZGrFMb1M`rF{fk)svsg~QNlhJij^``?tcr(kg_$6? z?UJX!r^0UcE5&K^D=txgY0poh7kjZ4A zi~1?{cSm18k}ssyJJ-!`wURb?j8mggGfqw(R;1UMW+rA(ew?UA%jX)Me%rBTM5(qb zDo{7z9EE@O8(X1gn2b``YP{!}L5F1wu^zt*N6>0Ru@Ry~Vewrm@>jRuFfM<-a3QWB z-Y%9BjqoZ)-u|1V6m#r=A41fO0-t}nbJQ_f%Q<}EH22OS6|uMBH~dQIKQKqp?w^3% z37@b-fwrO6tpUV4{jZoY&;4H@<HMH?JgRo&pzI#5Rxi}AsGu5w?jJ%ifRoH$;E2XVHKaiQ3q{f66YXR|(k>dl z)o2N`P@=FwTq?ELFRFphMkM8EuMK|Fp_dZIf6rz4mGfK59!I}3f2Hhhg(mPX zMxx*psYz`ZCV|$Cq9F5qUGA=pgz*_IEj`#3^LYs-X$}X>nq`MBZY=$_LxP?&=T5@E z=AI*D5;leiJu(HE3x<5Q>J=KbX>9q?gKZ@?;@067el27YO!+?c(NMzdd&iQ*F81-1 zk}s7HTXo#+uJ7)l-!l7=dSqf$B+1C@VRU1Dd0~1qCmP6OB?m#N>L))F9aA@YO=Jp@w%v5!Tf;oVvj$St9g<&8m}np z12@82%LABIupq?3X1seP=qIs(T~8p7=Vi+E#6Z@wY-~S#0-bZ}>letokc%e1-m?wZ zfVugUl53~u{uGJ`(EUP(mj%+d=}6CJ;4d>^&|@}~Bz=f=Zu*qsPu;weBZwj-If+2B zq4n`Na(qU0Q&jxye~R-4o1Gxd;J-B*&|Z9ydRlZGt}-dk4tOzU%WluloKyA`5ESy) z`g7Zj!sgZ&=a#`BE?!KeIwYgrxkY{%3mAbo8sfHw>rh zK5kbd=69t2pO3dV-*Ihkgj!ED8S&;}zTg=y$?6J&WJbbNsdQ~yett_AwjcFu4r3JZ zqQWm;(-|1(DPGKtB#qu2HB0^Q-x@gpB*K1Z3LNOp^Y2Q9)VCK}l+@r)skNFfix_Y;=uv;NL&inS|RaI2nltXPzOre%oUdi0kRb2@a zaKszS)9H^HSLw|%$n0I4e>)qv_&3+>FRIAb3ItV#dsacVBpFEsC04KPgquyg6p_gc z)^)DFZJHZX1QX)DVw|du0huu7I0?sr<{i$3>W)E z3f8kBPQwwnx`y9Xi67FM<+-CO*-i~v%rG#|LQNpB{aB+6&TG7} z%zg8tA98BNkUf<+*(3zTK#Vck{49*|9K1A>-PC;*stVm)H8gB7F8$;fVW@uRw~8!Q z(a;2cpQmH<-18HLpq&GuY2aKCy+tB~xFG{Kbc6x88gy{}*t7`pb#1+iJ?1k;hI9F1k+(evzfe60SkO|Z9VazLRmdQSO(JE1Hc>Mue$tpj}vM%3V z!tbYqW3K6U!LfN~`Xs#Q*L-7g^ zzL3@w=#TKVKManZOT&Boh(iM%@EELCD|<5{uY2LRg`kHcN6cABB#_pD!Jih4wp97FVx_&$IA6kmXcli_x9=nRqg4r4nL8nF|F0B`bLVR4AG#(K;+yWL;k&Y175Q1i%p z&q3V*{7+q7fr0H0@q{m*%O=0Y`Ut>@*c^NpM{T#ArljqlaVPNY>w=b!H@K96JpCRKGsx@U8*kg?@i;R#y6&Jx$jmi(d$JUIM?I z=*};y(f4jY`Pi<}zu08hbhY;K?%xsU#-JLY^etLfu6JmF<(9{N7vT?mVh8~AVLaP(CJ&zZk zZ6y{>Ah zJxn&W8E#-%FemT072z;?!ZgDBQOIFOb|+;mO9VqtN_gxf#~*h`W_3xoG1;?Zv5mq} z4UWNb;yA*w3gH9~DQ^f%9iq5yVR`+kO5qIsmG(nwtZX!k;XNZ&*(YW^57JDa!U!jq zd*QbW=Q=dnFOlsPlU+Sbx=Yj}%zj)*>*!hBzITKOhD14}>1iq&hJM=e?T@SQ?|HPI zb5M|_zqbtC3rSGdCVkLk{lhmPCzp^0B}FaMnC!^e%C3>;XUl@K4iAS+*rZqA`pvZU z+L8EEezfVMem%J2ZGWdthGFaBsO)ToaiK$+`vrH6v6OLc)pXv(huprcQ*kQnclcVJ zeYs-t0eCZ7&;a)J%>Ly05!wfac)}mtqPv#7sekc)FyhUcr4U&w2~Y6(spPrOkhu%Q zgpzQ3aQvP__XazM1s-~IVAj{e|6U@(jc=<^g8_Yvlt|h{z1N_2caP)k#X8#Fr@h)- zI>?!i%M zPG|g|hPhsQbx^L8(z(e@J}qTY+XBvNROZ^ygJF#hHfPy#Z`l7g!0}2JQ|88!L)`J z!U=SH+crLIXYfdimssNtI9C29W@}_c+6=!u87VEu*{ZH*WCruidqFZ*TG=~2Z`Dtq$@rX5k3 zDmvF6#5HFRv=d$PNOUK!SknOLx6`_IqWVmB)?Rfw)D%jddUG1ZsTHaSX_t?zQ*_{! z1^2q^-pZy$wN7la)!+6>XfVu)<>JKu>lMK4|H0(T*bCW9rU4@Sgb!UXi?!_B%4J*$ zZ9%2iO2||z^g2=!if-#%5$ap4y(s!7^2Kk?r;RVUqHB#b>7EkkNkHY9{u^?qPbCq6Bz@1sb_;3dNx zzv$V-GR|Db7V6ZZ45cDZdul0NBiuAkivN3$#x`2Zhje&AA7Oujf&mqW zQh*zC7nVY!w%E4gOWM2dJ(U!!{v^?^f!>K%AL@04C9dd-`}7?3FRGeu4Xw(U%|38z z{z2E^lrF3{h#X?H(O&zBY4StxwUEHk)fMIpmi5PQpmku+2my2MDycBs{OcO72kFl#b(^X5Csh^xq+!6jV+nog@pc4*am7?iJSy#jeN%Gn_Fauk7l17( zeLS=p2;<-QsM72S$f5$4RFx(M$54r(u+8g*NWTz#(q=C|XIGo8g~Y%Ul-rf%sS~%x zr3bWtFMJt;tvdCcn{fjni*QPpm)|hhdc7IQviI0L$iUr9OlYPxK&ZlJGRU;itIKwR z)}IUzEdu@@4 zQB|v@*pz4zRpaxV4m__g11Sh&XFQO_(CC#S#qNGMUp$B{68P!5OVJ9Z{WuVQ%+4BV zY_Cw7$KUuMIVkLGo=hmA@Mn-wkwC0f=xzAK4))-yQmZUd^-tpQ#rDGQqDQy{%rHHq zMaZpIF^C*od-Jd`}IocFAvs3JcWM3A!7KhSW9X%F~ZvA&n8!t67PCZ)c zKU=y~FRTqF%Ob)NnfZGCDztKCMDi&>s>+S-qW}5HgcnhnP(>0FLdY}moE{1LEVN@; zflo9-nUUNarX(L45YS_nv_nwL%T{rqD;S}*H33$yo}}ge$|E+YyGC@CMp|btjkP1N zUZm`y?#Y(2f#haNyy>$L^;?~U-B7m95PFRJ&yXk0l8ecBnNInH=vvU zl-e$iVcnXxIGQ@Gy?JGpR?}6!a@jLqt0qt>v7ACzt?U&mBjspi4HKRIQ<(EuM{HdtZ8Ajv^0GY}kXmv)` zvQ&5*HMrbGuq};bnEBJJb~`Ij#Mn$I97Tj)$I;xG+#4g`JBAS!o@u~P&PIcKY5H7O zdEW9e2tgFO?=SazpQ-O#K_HK-y~$lKLFbcH!T5~QPwHJ})?q1?rb5R0Z$rfo%5mt? zLa#u-0TIHRChu0J%TL5}lv$EIvzI$qKI={2R)3({&a{wP$Q${h7WBr_IssfG;t7>c zq_e`Hi6SD8h{;}lmR8-jN@_S{j)MDnfGzy^5m{!D-d{*QCUnsVjSeMyB>I(F;hGA> z#Hdbmb?bB-#ImF_J=ca_jMnCk4+UKqxIC}#n+ZJ<1$j2Cm5?`|9eE-12Mh)YZC<*a zADK%^Nd-Z7uHQXV%byP)wcu|#*T3POV2mXto?~E(?${19Iu>}d`?oQS0Iz95fr7K( z&0E}hSNfYLTQ!#`OWUjKsy^g*m?_%hP75DH*XKR2lzL~NQASo>vxj}fAxyFz5uMVR z>T=*W+!WSjU->bG(0kmq0p&Db$+CV(i%UzJp3u=siRIEhzrEt@W4;}Dz%liHlfy{> z@Cv2G9WSY;?RhwHy7;0TgdT)-OTDhlqJQF`)e|J$xbf%4{Uv1OwB^cV-{vmY3S<>M z(Nd+KQHWo$7&_EKp=(uiSC2$YiG-d<#2aK>RBvn_E0?0W+LDM_NgRtG?2=@8ks@}G zWmhe@p_o(w4W9m<#j55~!L_r?8g=aiixu${n>`W)q)~eJ^;w{Qz@+@!3O@Bdh zDm?XBzsZsU%2x>XVL6cz$sIZtX#MzRB`HF1qPeO@O?AR*6;4F?7Fx6)pfEMvu8^ST ztWlFh6gdr%8x8Dg+t|IoI{`c&O)Fs4p-=dtp?w%Wy2L?>+cCe%HQXy-Y!NF%`G7O} zEOz0BF>+9h8(ZkD&KjMz($5voIy`oJKkkoH6#CdPV5b>iXY!{${uqeZ7#z%(+Q?w3S2wtXT!u(EzZ5|kw=2-iOwD6dvmR@7o%VQ z3&KD>znXUR5Y|}2YB?K|SXhTtYq{?z`PY)CQ&TLsOC9Z4Wx}P}47|X>khxeiMoG!V z91b~XW^XpG&#OqxDE8y%`SlcCu)0ELFT4scU+nE4c77G0S}Q&Kzy8JRZyArLzkPN# zhiCuz>VMe7@*;hjotu1{R=yUl+or`CWxKA|R#!R8Rz~YnAGYiv^R1$la1)0D6E=8Z5#aEbgO!cqsG?}K-FgEI}6eR4#`ASL{% z0C*K(c5Aded*9yMU;hUHz$flzvyQy+UbcZeZAaC}15kT}o?G7WfwR7eEN9FrO{xV(F{{CQnHBI^|x z_)?(xeDCn;)#YgV5!ke`umaq2ix&X!?B85LJpc6d2bZJCkKcgb`-g7XCUW%eX7N$5zwF(?tS(&+5d2AhF(f_{U>w$$`CzGSCg6f>(%&Rcg0K#OOY{;) zMrBp^rtY;}W%E3SX5 z3pF%~sxZ}A$VH*PJmR+=^Lbj&y&{guCpf?H7ix_z0uqfJmF3Mrg1-|u&@xtYjT!!C z+~YC)fgrf{8vo?e|I5>RlOLND|3Fao0svk*Fn`_eH3tv<@!g%hx8J_q`knv7Hoexg z`l1Y3PhP8DM{On;M}7Kf!Jh|)>f^`rsN=kI;`}#pTRqP(=VqAe(0W06XlwP}^{eQ- z@)oM?Q_{LQee~5S4ko0kTj$U@`s$T=_%^^X{C%F;?#|oa_$^oeI9_MbUan0pdjSAo z;K9cUrqe$^o6hh)U(T$lujJcsx?-(ZOTM1f9uP@8uU{`$i7i)qi1|wWh+y@O1z)_-cUhpLhFKuAw9K0^l+D+F!VmSbX*KFTWK& z_5b}hZr&Kd+X3bk0jxN`;brrzWuo~yDBTeCVM8$th3;<+HLH>>gVseW_5YL7^btZcTbOQ-5f0zqwjzDcR&AD+{@v~yt>z`&PSiJ z7X}A=J3n&m%9R87l7Zs;2*Z@kRq7{c{bZ?Y@Qk{h*Qn>$744><*Uhmmt{>-0KNoiv zw(@&v-L`D4`rqNUY$E4f&bk}fU;pynHQc#@zplRrd-uWY@JEd5Q%|XTz3TkF@0*-G z`Q($c`TXq1?i?MT!-xNdYs5N~`kmr;%Oep zc&T8D@BP2$E${ij&d$y|?%cVv04Do|Z+{=+_IrQh`1X#I)3HaX zx<>cYN!RI7&|OZ!KGDy%O*ONI-A@KHv7WRQnv$u11;y2I~tc)tB3s zS1hJeu=R{6;>YJ@DPTJ3DVaIyy4n z1ss#Mcf9Jd>-=u(f*QKc_V#UCE2Cbzes)`k6(@B8*Kni#S+Tt_ea*FRjUm+^e zT`6~5Pu9(<(%jaabyIoPJ&#NMt>{3v2axra%xb~2T-;bp_;Fxj834MYc8|OK2i!tU$XX`I$ufD!G)UqrvtSU zl;>E-JUSKA5j<4wq|-5dM|k`B)KU1Zgtv671=GJd7w6s(`cSReLzPdpvsm1R0jtYm*H*m^p|NTWL-XaF zN(YlJ295IiwRpj{kcBW}<{i2nh@dRkpS18*anq(`6c;ac{o9kg@AoF7b^MYJ^$s2rPzT5Iv|wF`S}zMbC0+`Yg`C z6g5Yz$!yw)HFz=_j{qaS?43wM;%XV9FQ$eJ7u}dogjIq=M12uZF%XJsksc9vvJM1< zn1>hK0#h1*Ebi&!$z-&IJ)T3&uK_;#!AIx#$A2c%@%xYAy#lz23jqGBD0T6ZO>Vz;DafuD&TG)-7w7^V(D$ zTaiaLVk=70iU?<&7Ck@TDs^SKz1E~gs>NLoRha5b2?;E57>=EH6pMy@MbkbhVDE~a z@sbe68HNoAkv(F5{*whJ0hW}R9G3LL*T_-KPU49}fJN^FR16pgO(mwdB2slIYDv%y z3E(~bQ+RSeKRNmK@A%P&Uki-o@)sV1*91OhD;|$`-gj_#aP{o$4FCFHgJM#}h8^XW zY`B<8I$7%lXl`Xb+G#H5SQWSgw0B?&Pncx2TWV~<)Vk73BaqDd)}QTvDeMC3b zf=}v79ITiq77ZCNm>z={0mEMighVw93vtCTGiKQy`=UKX;$&Q|3yUEs8cLnY2)I%^ zhYv|oe258l#_chgc&v|~>BGH!_u$~_ax$~u2k^+p%oqI_iSc6mUikihfQ`52{eM5n zL5;XIt!tn1+RBo=%zCv+Wl@-e|73xK25>HNK60*uCcB%&WYg}VnccM$3u~lKQFR6m zSt^Vrdt^@_Hy3EC8nE<>NepT!Cj&66Z#$zJ?4! zLSKVMzOML)9)|ETz-TcaO=mmy6@aHc2EXkGw`YIj*@IWS;gz46OeW?p0K(DuL&-tI zT0@jn*2w3^3d-EL+A!96|VhKfibV|9i*yyT0p=`LzFoZ+`H#@Cne@o}Qks=$2&JV_Arqpk1mfGmyDl!-J6N|`sD1p1jFW%^Bq>Z?+n+m2m$#4k29D$UYPEMVI+y7~?Y2bGX6cb`6*VanUe#7)}M_(;-g5(uF{B4tQdB z1GVfAC1r`prOGlK#$lM&m13k+bYT<0CD!H$J_W3}0-p0TK<7^9!BsL`4@u(q?0>ln zoBeDteFS0V0^sQA&ci!9GyJju{~f_zV=cxi(<*cG<)*x(J$Z7kNO%3T#$Jg#lQ8da zxx3H1bHVo>;oO?K&cqq*#GN?&`e)Btl@gqNejC6iMI%TFw;#B;tglY;_yig`*wAvA zL#oHmNUf$OdB#g_EvVsgDrCDz*yiezH-Q@GmC?$$6VX;&LaaM_ zogr(hrzX5=-1Va%lkZ1FVnfSe4onjb!+krPc!t-26RU`Lx&zY`pDF2~(EP+r}U}Vm_Dxpi1`L7y+Rik9KNBbeCsrvLR?E|^F$;ZMy}X_y!6!o%%GAn5N-tF z#}F-BoI`e*0nrHPhO);f65+^&M}Jc`f28@F7+9}xiGgPYBgE7C0xGc#EpS`ojX5Tx z`PpbP8$XPD|M=6N{?e6idEn~r?98SQ9~~Vn=}Um}=^N7|RdYvKBcB^J%3PjB+LBk! zK}^XZZ~hqYZThGbIC930U@^f-t7%TP5ClM8b|&TZu@)t@}3HT*Q> zuxbEeL*FQmtk0ASE_}tp_s6Fuj(5bv_jk@E$|_J zHecX{z^m?i+XMGc?mOIh-R{op;0#_C*vvNor|mE`yZ_3im$_VL^LE$Ty5cuKBkeBM zwTkk3c2(O_{6(K+YyBCVekNzY>JqkM^EkZ*HJNl~C9?s;FFr#v2*!7&tTByig-MP? zh}{k4n55-JPNXX?&53;TqvE*`0r0rWjq4~{E=+Z$nKGON*s}TSTDGSssK-O95Dy%k z7C0d;I0BF#cuq*If^m;uEMTwS**Q2oz3+9C)A`~J@ag}(#d3if6}qZ<>0}+)OapPL z><^yK^h3TOHf}bhyxDoO{xr>N-UAWeh0gt3y*WO2XQ>Xwwm=n3Qk#NlE1MCSDY+q# zREB^rnox5Qznfv3c4e2jaVHv}sT3UL!E#EnQbKE}O%Iv(x8 zlm0hM7RU34rc)T=--5hHZnYM5-$1*zt}C7vhBkXToyl*HIIr-7Ir`?|x~pr{`_1!R z+Gz)Rc2K3M&NSuLdZPN-u=knDG~~3i0R!ulFDsP|xKeD~AtHBo+lTZmQqTopBH^Zi&N2!5z4HjF57Jzyn8y>Ul0+e1tccMjU5F%>w>F0NiTo01?xQ)$jRnLj!yCFg)w;+hAMQ<=Ped*S2_hiH`j4tI;t* zzEX$`sg+68GeuipB5^is<0~~Y4H?Y_49w{e%?Fy?j5b&$(7z^ zDBW|DHV9@k3DN1OXeG5Nn4}1XN%*Y=F^w!GgYhrlX5v=Q1sO)x$^y)kwb+2?RdAk* zxCo0Hwr(YeL~_#+ed>(>s~%Nx>*UfZ%mVD37=U=p6(%N}-tBuEH;mw@>2F~Wco6h% zZ+M0`T*#k0KEMovDQF7rdcEqc$taw>J#Z4_*TbqA716|%XpGO`n}0`((YH?E>fv-8pCpOErK8q?q{THt zB|fyCOxNx7k=!jB4c(+w^?LBTuSAa|nI-Fo5T-)GV)z5j8U%nUe*WDUz9i4H_<*S*#LJ@Te2!pa6IQ6xNO$SF$xO`AR%9(QpAI zdj~{SLv`9L5K8B+6o3-`CI-<(9DyW;yyj>^g*bvLVXM;sauCrc@WBfQr}!4Ye*7DN z4U#7JMf~q#%uOuSMQrnB%^f*aiTCC8KfTmI4 zv)s(giU1~XGpLM?;AJr4k)2@#mp}S5Oj?Z%dTthr0VSxoQHy>oM**dU1P{S$$H1sq zK?t8QhuRn|K$sIv3Pf%e^1ajgLo}4}H!+asiVu0iv%FzKb{^gr8x9k@aqR0Yr0~4l z+XEo7fNupJf9;uRkdbD4L z%q(S#v&jQ2qmyM80ztF@>JVC0uP49zFX2L>F1ZTW8&bDqsoH=_vHXpdzDS6;~pX z#jpS~6FzOsfVr3#0eF|NcM88xWbY%`2)ace9zsYI&bUZyxMN&LqFe=Z+xSsmNQ!}M zg9wR%tkQ+5n}0#V3;IR%p44C%W!W5lJes7;OV7paKz^yWXHG3=`1Vx0$x1q}9=nro zvQC*Rzn1n1{(Wxd!}?bontWPoONoPs#VeK#`ZZC6W>=)`7GJNrBr7^O&k$TttG-p4Htxi%JtxY^RCG7y)EWL64PSXG(QWX$%avC7qkG>FEW+Sn7)HtUKOgvy!uv!(}e3=BQ+d9SlSH)okEWk{R2tLPQFne<%aFG0%0Dvf8 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

-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�?Vp;tmh5e@jXD!0q-Th| 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 literal 0 HcmV?d00001 diff --git a/generated-icons/macos/AppIcon.iconset/icon_128x128.png b/generated-icons/macos/AppIcon.iconset/icon_128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..abb6b25291b240b919233fdf470fb8a43f6cf9fc GIT binary patch literal 9185 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 literal 0 HcmV?d00001 diff --git a/generated-icons/macos/AppIcon.iconset/icon_16x16@2x.png b/generated-icons/macos/AppIcon.iconset/icon_16x16@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..11c769e2fa47de12d88000365d48a72549e3f0ba GIT binary patch literal 1456 zcmV;h1yA~kP)!hvw-Y)JT1NQnOg;XtBt^^edK2PA?K zmaNL|zMnH)WvZ)tX8Q3K+1~8yo|&GmufD4Kx(5LOM8qLtc=6kdi<7;bTf%&;;pPel z9s&sJ9FX`6C}Lv~K$di!#~iPbxw{V{h;Hc|JqDx)%jx3opv!?3D8Tr3t)H?;yYtA-LV8qVXs;m)Y_T!I6Ds;U4-BM43NbUB@Uzx~BWKSmI` z_Tle`-%Pg8{oFJ)%;&R!h(w6kH{P-!u)+A6VIB9b^(E1U$iwlLlfeLHv+1{!&n`d2 zt*xna4^_kEarg_UCVdwTl&{P^u6yHrr3veYz`m@5d?%#ZEd?%-5iW)@CmKv z&F!7Z&c1qX##yHVw!Kl<(UB0f82eytetw?Wp%5Y)I#+G1=JPO`O!j9|-}ZoVeX*Pi z5uq&ry@d(_%BFOfW4#FmSN{yPdW|6Jmp!)UL>N{}rSG~2fJ=3~(tsx?v)9~SnT?Wz zI6Z<$2;vIXOB@q0>8u_?qyz}5ZW<7zOCA9$&Gz->H@6OE>%MwL1fa1-wWvJ;=)Htb zLlJQ%aH2pAWaT#*k+?W@A<)U96`eHzK)8&v31OdkLgaW2U1(T)%}!1mr2RO7)LgA|rE96vgM z$~oXHSbgJh$$|kip|BKfq4cvtz0so71$EB+cm|>280z(M42uSU9B>YTt;-n@^wQv$Hdl)Q-GW3fd8g~>t(G$&ZNz2lq* z8hRBK3t%rN%xg=I*sYb9`I`WM0*KkYDVp}C`K|PKD=})$S!v}M=cm!@h-*X17Mf+( zKIp^+m;kR$&R~fXfVA}UKaQYY1aQu3Mh$!CZP5f` zV_Wk+0WiZ!*b2m1-Zfi;2EM)Tz_|*XQ{(n;318NIDczf!*M+0;E{RJ3QCSgUsM510 z2-G!0rBaJZy-BbpU<9N;r}I#hh7YXXClCohj$;O(n`!^~JMWEvgGqiSHLuyM0xXXj zAUkaRZ}S=g%>(tY4MS??mEF_lYEZ8yJEzNWy=q|oY6TQk{tYa23}6J%h9Y^{(w}3C z4xT+*!f>!C;;!3P>`jdTA~W<>#uEc(yjcrHHf{?eI0Bf?Jj~||9tAu}yr)cSZ?V_0 z&c%%?cxnh5P9QRX^m1O@PPPD*)5Qr~G_GS#AWQ=Y(*|{h<^xO3;HpNX1(F$HvRyGA zbYiJW1ZYf|(!~Q%JbKyQ|C$P)(zIekxF!~S;+z*|WHnwh!YU{$R{MI=%3*2Cb%SP{ zv2j&Z3jc}b(EVx%7+B&dOUts30D7fYNOgO5E$p8K3p2}TI8Xxb6I6pc)06sOIPy+J zLjDFgjl#3uf6cgX7>`Hp=y-ZC8cy!eSD)g6Am1ER2;-4=gd8dg60`v(l0000< KMNUMnLSTZkNxqN( literal 0 HcmV?d00001 diff --git a/generated-icons/macos/AppIcon.iconset/icon_256x256.png b/generated-icons/macos/AppIcon.iconset/icon_256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..2169c7c6b1e0ccab1840d241307d06daacdbdd6c GIT binary patch literal 21502 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 literal 0 HcmV?d00001 diff --git a/generated-icons/macos/AppIcon.iconset/icon_32x32.png b/generated-icons/macos/AppIcon.iconset/icon_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..11c769e2fa47de12d88000365d48a72549e3f0ba GIT binary patch literal 1456 zcmV;h1yA~kP)!hvw-Y)JT1NQnOg;XtBt^^edK2PA?K zmaNL|zMnH)WvZ)tX8Q3K+1~8yo|&GmufD4Kx(5LOM8qLtc=6kdi<7;bTf%&;;pPel z9s&sJ9FX`6C}Lv~K$di!#~iPbxw{V{h;Hc|JqDx)%jx3opv!?3D8Tr3t)H?;yYtA-LV8qVXs;m)Y_T!I6Ds;U4-BM43NbUB@Uzx~BWKSmI` z_Tle`-%Pg8{oFJ)%;&R!h(w6kH{P-!u)+A6VIB9b^(E1U$iwlLlfeLHv+1{!&n`d2 zt*xna4^_kEarg_UCVdwTl&{P^u6yHrr3veYz`m@5d?%#ZEd?%-5iW)@CmKv z&F!7Z&c1qX##yHVw!Kl<(UB0f82eytetw?Wp%5Y)I#+G1=JPO`O!j9|-}ZoVeX*Pi z5uq&ry@d(_%BFOfW4#FmSN{yPdW|6Jmp!)UL>N{}rSG~2fJ=3~(tsx?v)9~SnT?Wz zI6Z<$2;vIXOB@q0>8u_?qyz}5ZW<7zOCA9$&Gz->H@6OE>%MwL1fa1-wWvJ;=)Htb zLlJQ%aH2pAWaT#*k+?W@A<)U96`eHzK)8&v31OdkLgaW2U1(T)%}!1mr2RO7)LgA|rE96vgM z$~oXHSbgJh$$|kip|BKfq4cvtz0so71$EB+cm|>280z(M42uSU9B>YTt;-n@^wQv$Hdl)Q-GW3fd8g~>t(G$&ZNz2lq* z8hRBK3t%rN%xg=I*sYb9`I`WM0*KkYDVp}C`K|PKD=})$S!v}M=cm!@h-*X17Mf+( zKIp^+m;kR$&R~fXfVA}UKaQYY1aQu3Mh$!CZP5f` zV_Wk+0WiZ!*b2m1-Zfi;2EM)Tz_|*XQ{(n;318NIDczf!*M+0;E{RJ3QCSgUsM510 z2-G!0rBaJZy-BbpU<9N;r}I#hh7YXXClCohj$;O(n`!^~JMWEvgGqiSHLuyM0xXXj zAUkaRZ}S=g%>(tY4MS??mEF_lYEZ8yJEzNWy=q|oY6TQk{tYa23}6J%h9Y^{(w}3C z4xT+*!f>!C;;!3P>`jdTA~W<>#uEc(yjcrHHf{?eI0Bf?Jj~||9tAu}yr)cSZ?V_0 z&c%%?cxnh5P9QRX^m1O@PPPD*)5Qr~G_GS#AWQ=Y(*|{h<^xO3;HpNX1(F$HvRyGA zbYiJW1ZYf|(!~Q%JbKyQ|C$P)(zIekxF!~S;+z*|WHnwh!YU{$R{MI=%3*2Cb%SP{ zv2j&Z3jc}b(EVx%7+B&dOUts30D7fYNOgO5E$p8K3p2}TI8Xxb6I6pc)06sOIPy+J zLjDFgjl#3uf6cgX7>`Hp=y-ZC8cy!eSD)g6Am1ER2;-4=gd8dg60`v(l0000< KMNUMnLSTZkNxqN( literal 0 HcmV?d00001 diff --git a/generated-icons/macos/AppIcon.iconset/icon_32x32@2x.png b/generated-icons/macos/AppIcon.iconset/icon_32x32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..444b1ed26e42815fa444a8914ee522ebc6e6403c GIT binary patch literal 3608 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 literal 0 HcmV?d00001 diff --git a/generated-icons/macos/AppIcon.iconset/icon_512x512@2x.png b/generated-icons/macos/AppIcon.iconset/icon_512x512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a0785f066851a354bed7311ec898a96d62a0e1c4 GIT binary patch literal 82303 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Û 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�GSe(Gi^V)8qSzK+goY?bJ( z@iJ0ANsvv|A~LeVT)}|jFZU6uqUoi{|BY{&8@o$?9~p9U4t_4c1PRlJX95z>;xHC{ z%izZwLHc~JPk?pv&XoJ#iIKUdD>pVB?M||O%1aC+4_1^Ig_#Um0dqkZU7V-vbwY1&u9LYIuv1hkhOTvy}v@!nlj{~Fq| zkX_4xj!VlicxNlMCgPnL& z)1cJ%-LnPt=4T#UpSdO(#n-`OsHd2n(+Q)2f_iExf`JL_$EcB=0Vj4Bf|~9}{pF%hN2A0uM#o6xyj}pI18NfJ8i@1a#&=K1PV9O-)Jp6UEb`F!FZ-Z%UHC8GJk8t{TL9=LfFp*AwPS zvMPofUMghP!B^`fEeghYu1r_{7IbiM@KesWTePhlDOZTcDHn8)GqLGt)Eqj|B$4;ZI57)23 zTp+l!gT&GGs=Ek?>MR^SmLcP_|2Nyl3e7)a@}en6KYc5P(PE}LB$Z%~QBuQ7>}s|A zrf5XE+ZxGs#+;RN`7=841`!2$^^1*RrmtgmujIDP@48J9Rs1}KZI+m9(n%mJPoV_) U=}P3;zed)&rF*mRy2Y#i2g?a7YXATM literal 0 HcmV?d00001 diff --git a/generated-icons/web/apple-touch-icon.png b/generated-icons/web/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4fe73fcbe9f80dc4d5045cbf82b1b5d625fe8e8e GIT binary patch literal 13991 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 DR@V?w literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-128.png b/generated-icons/web/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..abb6b25291b240b919233fdf470fb8a43f6cf9fc GIT binary patch literal 9185 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 IM6N<$f)96swg3PC literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-16.png b/generated-icons/web/icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..2270f0ba26c5889a648613539693517f3186d0f9 GIT binary patch literal 595 zcmV-Z0<8UsP)_v8 zT6Vi!E}H=0`s(__!NKy=RLZG{1eg&iK&l{7ut;+fvA`^0k*>-xd|bXh{Y=+iH}7&z zUkHX_=s`q4E4JCaxC%n8V0WpAU52EDBK1ApT-|=lT{;M)F|>j>D1cVU zcA!vEWTO@f9KA?5ezj^bZ+(ATPUR_BdZ0nt?Kh}?^fpZF^0-S hm;Lbj?C9m;#UBS?IPRG~+}8jA002ovPDHLkV1m9l52OG9 literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-192.png b/generated-icons/web/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..4855a540de8ba5e853670ed30a3448e7fc54f716 GIT binary patch literal 15290 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

NT~K#2(Q-o~*d~v~)eS1c*>r8f?)`V? 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 M07*qoM6N<$f`C$q82|tP literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-32.png b/generated-icons/web/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..11c769e2fa47de12d88000365d48a72549e3f0ba GIT binary patch literal 1456 zcmV;h1yA~kP)!hvw-Y)JT1NQnOg;XtBt^^edK2PA?K zmaNL|zMnH)WvZ)tX8Q3K+1~8yo|&GmufD4Kx(5LOM8qLtc=6kdi<7;bTf%&;;pPel z9s&sJ9FX`6C}Lv~K$di!#~iPbxw{V{h;Hc|JqDx)%jx3opv!?3D8Tr3t)H?;yYtA-LV8qVXs;m)Y_T!I6Ds;U4-BM43NbUB@Uzx~BWKSmI` z_Tle`-%Pg8{oFJ)%;&R!h(w6kH{P-!u)+A6VIB9b^(E1U$iwlLlfeLHv+1{!&n`d2 zt*xna4^_kEarg_UCVdwTl&{P^u6yHrr3veYz`m@5d?%#ZEd?%-5iW)@CmKv z&F!7Z&c1qX##yHVw!Kl<(UB0f82eytetw?Wp%5Y)I#+G1=JPO`O!j9|-}ZoVeX*Pi z5uq&ry@d(_%BFOfW4#FmSN{yPdW|6Jmp!)UL>N{}rSG~2fJ=3~(tsx?v)9~SnT?Wz zI6Z<$2;vIXOB@q0>8u_?qyz}5ZW<7zOCA9$&Gz->H@6OE>%MwL1fa1-wWvJ;=)Htb zLlJQ%aH2pAWaT#*k+?W@A<)U96`eHzK)8&v31OdkLgaW2U1(T)%}!1mr2RO7)LgA|rE96vgM z$~oXHSbgJh$$|kip|BKfq4cvtz0so71$EB+cm|>280z(M42uSU9B>YTt;-n@^wQv$Hdl)Q-GW3fd8g~>t(G$&ZNz2lq* z8hRBK3t%rN%xg=I*sYb9`I`WM0*KkYDVp}C`K|PKD=})$S!v}M=cm!@h-*X17Mf+( zKIp^+m;kR$&R~fXfVA}UKaQYY1aQu3Mh$!CZP5f` zV_Wk+0WiZ!*b2m1-Zfi;2EM)Tz_|*XQ{(n;318NIDczf!*M+0;E{RJ3QCSgUsM510 z2-G!0rBaJZy-BbpU<9N;r}I#hh7YXXClCohj$;O(n`!^~JMWEvgGqiSHLuyM0xXXj zAUkaRZ}S=g%>(tY4MS??mEF_lYEZ8yJEzNWy=q|oY6TQk{tYa23}6J%h9Y^{(w}3C z4xT+*!f>!C;;!3P>`jdTA~W<>#uEc(yjcrHHf{?eI0Bf?Jj~||9tAu}yr)cSZ?V_0 z&c%%?cxnh5P9QRX^m1O@PPPD*)5Qr~G_GS#AWQ=Y(*|{h<^xO3;HpNX1(F$HvRyGA zbYiJW1ZYf|(!~Q%JbKyQ|C$P)(zIekxF!~S;+z*|WHnwh!YU{$R{MI=%3*2Cb%SP{ zv2j&Z3jc}b(EVx%7+B&dOUts30D7fYNOgO5E$p8K3p2}TI8Xxb6I6pc)06sOIPy+J zLjDFgjl#3uf6cgX7>`Hp=y-ZC8cy!eSD)g6Am1ER2;-4=gd8dg60`v(l0000< KMNUMnLSTZkNxqN( literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-384.png b/generated-icons/web/icon-384.png new file mode 100644 index 0000000000000000000000000000000000000000..15659341e0bdaa7cc5e36d630c0961bd8c0e30c5 GIT binary patch literal 36342 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%0A89y$Dz*bK^7+e=nc#VM!K|Vi5QY9r_lK-u! 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

v2KID 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 O00009>^KO-9yAvXCJ$;v_FACQH! zuwcOo5J*`d5(Gw;Mjnst@yvAhbk)5Vo%=kux_V|j4p(m1t?GJw=ljm8b zgPP|@|2=vWmHJTY?mc778`_vn06;7*uwm;rnD#9oPu+f=yYItR&UwUrKdeQhL#5~& zRMl5T?&#`r$twe*enm)!!Lwe!bR=#*4Ovwq1ZR29`#Cp1)B9 z8jeXrR)NPj=ZnSlLF3`?^2!JE>h{Jr>g$p2fd zVZQ&Q8;vJ7aee&d^v@4}g+IkVSjKLxrQ?4eU)L4Hk)Z8QqFfz1r96xzl?z; zmW=>y@;o0GvETq8SVXWyV64^6c%nLM_8O{x0{5;zCPXwR(0;nLwa(x>2Jaa1NCe8^ zPd+!hQs3|#A3`M&KTs%PfcV4SYy5uvju6$!eA+b|Ti2%T>{I+L8a({Z;jIqLUlFV_ zMmyOOpaiC;gcZ4{urX#7$vzA+4}}UwY{SP|c7Cwr*-vvMi%MBTq^YXjqSok-HYV$} z*4mb7QZm_hBdNlGfoBY9|KM7CG4*!|r<<_Z{mo3Fgo?ZO7-`t`OjL&hrRBL+(6b53BFrIWLv-07WvL+^aOxhgc4NXBsivA|b?h7~s#5 zR~X_|2?q26OZcs4J@P0Z3GF-0g(%xBi&{*jxpDJFj^{+|GMVM%JMLQNBAMJUmj)~; z;Mt9i;sGz`nzBq8YhkoK0a6@&qAwDgrQ*eVoq_hr5s>q(2wtcs``%cY#5Tr-Gaxb> zIbLRcKvUlY0w{#_D;sc;v3M~9JJ;?W+5d+lGAd9Q;}yv6Q@BXJbl_N4ffXpc?O^+(#yaL&cB@p+{PRCse3hAzAYFJeR4nT#e;cE)F(e`fLu3szbpln>M+2AokxnoEHmL zL16ExsZS_X=qV6Azi&wIwkfCkY?Vbps7E8Ht0n{PDIu4gHniO_=E~}|Y(sKg(IZuv zB-uy|J5L5Ao6o+HQC^W1QwazLr$mEiw}8d%WlRJEjOgJqpF`j!+oA=KeTxO?S_354 z3*jO{bOGxWz`;9$jHe+>qz4QrkinF!3M=ziz&fGrZ*?v|C z`BIjG(o6fXf$TzMk<9V7ItFB}kX)ZNVr)fFC}Box`iRMtj-r)FC^ftbhl` zY4q8XBNv=SuZjTOi2)>4WDuJkmaQ40V-QlfPZgG@x#KU}n5Ft0{WcoldYSNT+ZuTK z_yn4&T3Q)aNC%fre35U2?~UlD63paG>8rm7a$4-^d88)5r1NhCSR#!8nRJd^B9o9m+n;FF}jZf z?v8|a;8?@%Zp&rNWgHG#6}JB4=joMB2xyLzrs*xoFJ^!TuT3;0XC>JVu>#C^UIU=J zP!Z?;26#;$0V@GKf==`3j**1#)`dzn68`kUfM139R1#Y}|K>UL`V>!=TW^dJ^EmK3 zh#l7pMZh{5;eT(CU61dASpoind?{i8yqlMVxhM0b%&WC(!79f^qoyi92#dejOQ8dr5L~wCzWrjiZg+AZ%AA`FxUW`jfnd)$V<>= z{cxjaM9ylj?l%^!sq2Pk-*U;kuPXn%n0;(gAh0jN3zk!cizIUjfT5hmlPQr zek2QL#s;idH}=M<4)K4e(8d_OeR)FK=(|^bF#4K4`NYy_Qh$7W)a{Me>I$sUF=tm~ z-6!UC@mfCPTlEU(9LMk_ZDjD{@mh88VzxIP)5rW1G2OZa_|A`TS9kV(MRvY6u9XGT zDF#k#M5wHxQ28PxjFBDZfMKy<4FbiN0^g0tV?~7X#iDzE1(+{q_w*4Ufd4x-*44#d+{qy?4v#0a7Pv+*cQDbM9wkkClS5#Mo4mg#d z`U)&BA76rGEO&_rN24{`+T2uiUCoYq|zNec!z0dC?1r{>XJ1D-2&|pPicy6{&BC=qprkcjLy$|GA9-&!_$mo__oi TN*9dI00000NkvXXu0mjfcwU~8 literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-512.png b/generated-icons/web/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..7f4b8110f8f2b83be0d292718bd1c33891484967 GIT binary patch literal 49433 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 literal 0 HcmV?d00001 diff --git a/generated-icons/web/icon-72.png b/generated-icons/web/icon-72.png new file mode 100644 index 0000000000000000000000000000000000000000..60283bac03c69744eecb6cf27fd1a6e541528982 GIT binary patch literal 4228 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 mt5>hq7cN{-|4iNs`~Dva>zWKr&eYrh0000 0: + sd[sx, sy] = (25, 18, 12, int(a * 0.15)) + shadow = shadow.filter(ImageFilter.GaussianBlur(radius=int(RENDER * 0.01))) + icon = Image.alpha_composite(icon, shadow) + + # White E glyph + glyph = Image.new("RGBA", (RENDER, RENDER), (0, 0, 0, 0)) + draw_e_rounded(ImageDraw.Draw(glyph), cx, cy, RENDER) + icon = Image.alpha_composite(icon, glyph) + + # Downscale to 1024 + source = icon.resize((FINAL, FINAL), Image.LANCZOS) + src_path = os.path.join(OUT_DIR, "icon-source-1024.png") + source.save(src_path, "PNG") + print(f" Source: {src_path}") + + # ---- Generate all platform sizes from source ---- + print("Generating platform sizes...") + + ios_dir = os.path.join(OUT_DIR, "ios") + android_dir = os.path.join(OUT_DIR, "android") + web_dir = os.path.join(OUT_DIR, "web") + macos_dir = os.path.join(OUT_DIR, "macos") + for d in [ios_dir, android_dir, web_dir, macos_dir]: + os.makedirs(d, exist_ok=True) + + # iOS + ios_sizes = [40, 60, 58, 87, 80, 120, 180, 76, 152, 167, 1024] + for s in ios_sizes: + resized = source.resize((s, s), Image.LANCZOS) + resized.save(os.path.join(ios_dir, f"icon-{s}.png"), "PNG") + print(f" iOS: {len(ios_sizes)} sizes") + + # Write Contents.json for Xcode + contents = { + "images": [ + {"size": "20x20", "idiom": "iphone", "filename": "icon-40.png", "scale": "2x"}, + {"size": "20x20", "idiom": "iphone", "filename": "icon-60.png", "scale": "3x"}, + {"size": "29x29", "idiom": "iphone", "filename": "icon-58.png", "scale": "2x"}, + {"size": "29x29", "idiom": "iphone", "filename": "icon-87.png", "scale": "3x"}, + {"size": "40x40", "idiom": "iphone", "filename": "icon-80.png", "scale": "2x"}, + {"size": "40x40", "idiom": "iphone", "filename": "icon-120.png", "scale": "3x"}, + {"size": "60x60", "idiom": "iphone", "filename": "icon-120.png", "scale": "2x"}, + {"size": "60x60", "idiom": "iphone", "filename": "icon-180.png", "scale": "3x"}, + {"size": "76x76", "idiom": "ipad", "filename": "icon-76.png", "scale": "1x"}, + {"size": "76x76", "idiom": "ipad", "filename": "icon-152.png", "scale": "2x"}, + {"size": "83.5x83.5", "idiom": "ipad", "filename": "icon-167.png", "scale": "2x"}, + {"size": "1024x1024", "idiom": "ios-marketing", "filename": "icon-1024.png", "scale": "1x"}, + ], + "info": {"version": 1, "author": "xcode"}, + } + with open(os.path.join(ios_dir, "Contents.json"), "w") as f: + json.dump(contents, f, indent=2) + + # Android + android_sizes = {"mdpi": 48, "hdpi": 72, "xhdpi": 96, "xxhdpi": 144, "xxxhdpi": 192} + for density, s in android_sizes.items(): + resized = source.resize((s, s), Image.LANCZOS) + resized.save(os.path.join(android_dir, f"ic_launcher_{density}.png"), "PNG") + source.resize((512, 512), Image.LANCZOS).save( + os.path.join(android_dir, "playstore-512.png"), "PNG" + ) + print(f" Android: {len(android_sizes) + 1} sizes") + + # Web + PWA + web_sizes = [16, 32, 48, 72, 96, 128, 144, 152, 192, 384, 512] + for s in web_sizes: + resized = source.resize((s, s), Image.LANCZOS) + resized.save(os.path.join(web_dir, f"icon-{s}.png"), "PNG") + # Apple touch icon + source.resize((180, 180), Image.LANCZOS).save( + os.path.join(web_dir, "apple-touch-icon.png"), "PNG" + ) + # Favicon .ico (multi-size) + ico_16 = source.resize((16, 16), Image.LANCZOS) + ico_32 = source.resize((32, 32), Image.LANCZOS) + ico_48 = source.resize((48, 48), Image.LANCZOS) + ico_16.save(os.path.join(web_dir, "favicon.ico"), format="ICO", sizes=[(16, 16), (32, 32), (48, 48)]) + print(f" Web/PWA: {len(web_sizes) + 2} assets") + + # macOS .iconset (for iconutil → .icns) + iconset_dir = os.path.join(macos_dir, "AppIcon.iconset") + os.makedirs(iconset_dir, exist_ok=True) + macos_sizes = { + "icon_16x16.png": 16, + "icon_16x16@2x.png": 32, + "icon_32x32.png": 32, + "icon_32x32@2x.png": 64, + "icon_128x128.png": 128, + "icon_128x128@2x.png": 256, + "icon_256x256.png": 256, + "icon_256x256@2x.png": 512, + "icon_512x512.png": 512, + "icon_512x512@2x.png": 1024, + } + for name, s in macos_sizes.items(): + resized = source.resize((s, s), Image.LANCZOS) + resized.save(os.path.join(iconset_dir, name), "PNG") + print(f" macOS: {len(macos_sizes)} sizes in AppIcon.iconset") + + print("\nDone! All icons in: generated-icons/") + print("To build macOS .icns: iconutil -c icns generated-icons/macos/AppIcon.iconset") + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_prd_site_rendering.sh b/scripts/verify_prd_site_rendering.sh index 7cc45b6..8e3c4b8 100755 --- a/scripts/verify_prd_site_rendering.sh +++ b/scripts/verify_prd_site_rendering.sh @@ -3,6 +3,3 @@ set -euo pipefail echo "Verifying PRD live sites through the GPUI web surface adapter" cargo test -p ely_app --features live-site-smoke --all-targets -- --test-threads=1 --nocapture - -echo "Verifying PRD live sites through Servo sidecar RGBA snapshots" -cargo test -p ely_servo_host --features servo-engine --test sidecar -- --test-threads=1 --nocapture diff --git a/third_party/gpui/Cargo.toml b/third_party/gpui/Cargo.toml index 9f20281..a482f0f 100644 --- a/third_party/gpui/Cargo.toml +++ b/third_party/gpui/Cargo.toml @@ -167,10 +167,6 @@ version = "0.2" [dependencies.log] version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] [dependencies.lyon] version = "1.0" @@ -499,10 +495,6 @@ version = "0.5" [target.'cfg(target_os = "macos")'.dependencies.log] version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] [target.'cfg(target_os = "macos")'.dependencies.media] version = "0.2.2" diff --git a/third_party/gpui/src/elements/mod.rs b/third_party/gpui/src/elements/mod.rs index bfbc08b..169720e 100644 --- a/third_party/gpui/src/elements/mod.rs +++ b/third_party/gpui/src/elements/mod.rs @@ -6,6 +6,7 @@ mod div; mod image_cache; mod img; mod list; +mod native_surface; mod surface; mod svg; mod text; @@ -19,6 +20,7 @@ pub use div::*; pub use image_cache::*; pub use img::*; pub use list::*; +pub use native_surface::*; pub use surface::*; pub use svg::*; pub use text::*; diff --git a/third_party/gpui/src/elements/native_surface.rs b/third_party/gpui/src/elements/native_surface.rs new file mode 100644 index 0000000..28f9dd3 --- /dev/null +++ b/third_party/gpui/src/elements/native_surface.rs @@ -0,0 +1,109 @@ +use crate::{ + App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, + NativeSurfaceHandle, Pixels, Style, StyleRefinement, Styled, Window, +}; +use refineable::Refineable; + +/// A native platform surface hosted inside a GPUI layout box. +pub struct NativeSurface { + id: ElementId, + on_surface: Option, &mut Window, &mut App)>>, + style: StyleRefinement, +} + +/// Create a native platform surface element. +pub fn native_surface( + id: impl Into, + on_surface: impl FnMut(NativeSurfaceHandle, Bounds, &mut Window, &mut App) + 'static, +) -> NativeSurface { + NativeSurface { id: id.into(), on_surface: Some(Box::new(on_surface)), style: Default::default() } +} + +impl Element for NativeSurface { + type RequestLayoutState = (); + type PrepaintState = (); + + fn id(&self) -> Option { + Some(self.id.clone()) + } + + fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { + None + } + + fn request_layout( + &mut self, + _global_id: Option<&GlobalElementId>, + _inspector_id: Option<&InspectorElementId>, + window: &mut Window, + cx: &mut App, + ) -> (LayoutId, Self::RequestLayoutState) { + let mut style = Style::default(); + style.refine(&self.style); + let layout_id = window.request_layout(style, [], cx); + (layout_id, ()) + } + + fn prepaint( + &mut self, + _global_id: Option<&GlobalElementId>, + _inspector_id: Option<&InspectorElementId>, + _bounds: Bounds, + _request_layout: &mut Self::RequestLayoutState, + _window: &mut Window, + _cx: &mut App, + ) -> Self::PrepaintState { + } + + fn paint( + &mut self, + global_id: Option<&GlobalElementId>, + _inspector_id: Option<&InspectorElementId>, + bounds: Bounds, + _: &mut Self::RequestLayoutState, + _: &mut Self::PrepaintState, + window: &mut Window, + cx: &mut App, + ) { + let Some(global_id) = global_id else { + return; + }; + let Some(on_surface) = self.on_surface.as_mut() else { + return; + }; + let Some(surface) = window.with_element_state::( + global_id, + |state, window| { + let surface = state + .and_then(|state| state.surface) + .or_else(|| window.create_native_surface()); + if let Some(surface) = surface { + window.sync_native_surface(&surface, bounds); + return (Some(surface.clone()), NativeSurfaceState { surface: Some(surface) }); + } + (None, NativeSurfaceState { surface: None }) + }, + ) else { + return; + }; + on_surface(surface, bounds, window, cx); + } +} + +impl IntoElement for NativeSurface { + type Element = Self; + + fn into_element(self) -> Self::Element { + self + } +} + +impl Styled for NativeSurface { + fn style(&mut self) -> &mut StyleRefinement { + &mut self.style + } +} + +struct NativeSurfaceState { + surface: Option, +} diff --git a/third_party/gpui/src/platform.rs b/third_party/gpui/src/platform.rs index 047a005..1f89980 100644 --- a/third_party/gpui/src/platform.rs +++ b/third_party/gpui/src/platform.rs @@ -48,15 +48,25 @@ use async_task::Runnable; use futures::channel::oneshot; use image::codecs::gif::GifDecoder; use image::{AnimationDecoder as _, Frame}; -use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; +#[cfg(target_os = "macos")] +use objc::sel; +#[cfg(target_os = "macos")] +use objc::sel_impl; +use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle}; use schemars::JsonSchema; use seahash::SeaHasher; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::borrow::Cow; +#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux", target_os = "freebsd"))] +use std::ffi::c_void; use std::hash::{Hash, Hasher}; use std::io::Cursor; +#[cfg(target_os = "windows")] +use std::num::NonZeroIsize; use std::ops; +#[cfg(any(target_os = "macos", target_os = "linux", target_os = "freebsd"))] +use std::ptr::NonNull; use std::time::{Duration, Instant}; use std::{ fmt::{self, Debug}, @@ -457,6 +467,240 @@ pub(crate) struct RequestFrameOptions { pub(crate) force_render: bool, } +/// A platform child surface that can be handed to an embedded renderer. +#[derive(Clone, Debug)] +pub struct NativeSurfaceHandle { + inner: Arc, +} + +#[derive(Debug)] +enum NativeSurfaceHandleInner { + #[cfg(target_os = "macos")] + MacOS { appkit_ns_view: NonNull }, + #[cfg(target_os = "windows")] + Windows { hwnd: isize }, + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + X11 { + connection: usize, + screen_id: i32, + window_id: u32, + visual_id: u32, + }, + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "wayland"))] + Wayland { surface: usize, display: usize }, +} + +impl NativeSurfaceHandle { + #[cfg(target_os = "macos")] + pub(crate) fn from_appkit_ns_view(appkit_ns_view: NonNull) -> Self { + Self { inner: Arc::new(NativeSurfaceHandleInner::MacOS { appkit_ns_view }) } + } + + #[cfg(target_os = "windows")] + pub(crate) fn from_win32_hwnd(hwnd: isize) -> Self { + Self { inner: Arc::new(NativeSurfaceHandleInner::Windows { hwnd }) } + } + + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + pub(crate) fn from_xcb_window( + connection: usize, + screen_id: i32, + window_id: u32, + visual_id: u32, + ) -> Self { + Self { + inner: Arc::new(NativeSurfaceHandleInner::X11 { + connection, + screen_id, + window_id, + visual_id, + }), + } + } + + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "wayland"))] + pub(crate) fn from_wayland_surface(surface: usize, display: usize) -> Self { + Self { inner: Arc::new(NativeSurfaceHandleInner::Wayland { surface, display }) } + } + + /// Returns the backing `NSView` pointer on macOS. + #[cfg(target_os = "macos")] + #[must_use] + pub fn appkit_ns_view(&self) -> NonNull { + match self.inner.as_ref() { + NativeSurfaceHandleInner::MacOS { appkit_ns_view } => *appkit_ns_view, + } + } + + /// Returns the backing Win32 `HWND` value on Windows. + #[cfg(target_os = "windows")] + #[must_use] + pub fn win32_hwnd(&self) -> isize { + match self.inner.as_ref() { + NativeSurfaceHandleInner::Windows { hwnd } => *hwnd, + } + } + + /// Returns the backing XCB window id on X11. + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + #[must_use] + pub(crate) fn xcb_window_id(&self) -> u32 { + match self.inner.as_ref() { + NativeSurfaceHandleInner::X11 { window_id, .. } => *window_id, + #[cfg(feature = "wayland")] + NativeSurfaceHandleInner::Wayland { .. } => 0, + } + } + + /// Returns a stable identity for the lifetime of this native surface. + #[must_use] + pub fn identity(&self) -> usize { + match self.inner.as_ref() { + #[cfg(target_os = "macos")] + NativeSurfaceHandleInner::MacOS { appkit_ns_view } => appkit_ns_view.as_ptr() as usize, + #[cfg(target_os = "windows")] + NativeSurfaceHandleInner::Windows { hwnd } => *hwnd as usize, + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + NativeSurfaceHandleInner::X11 { window_id, .. } => *window_id as usize, + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "wayland"))] + NativeSurfaceHandleInner::Wayland { surface, .. } => *surface, + } + } +} + +impl Drop for NativeSurfaceHandleInner { + fn drop(&mut self) { + match self { + #[cfg(target_os = "macos")] + Self::MacOS { appkit_ns_view } => unsafe { + let view = appkit_ns_view.as_ptr() as *mut objc::runtime::Object; + let _: () = objc::msg_send![view, removeFromSuperview]; + let _: () = objc::msg_send![view, release]; + }, + #[cfg(target_os = "windows")] + Self::Windows { hwnd } => unsafe { + let _ = ::windows::Win32::UI::WindowsAndMessaging::DestroyWindow( + ::windows::Win32::Foundation::HWND(*hwnd as *mut c_void), + ); + }, + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + Self::X11 { connection, window_id, .. } => { + if let Some(connection) = NonNull::new(*connection as *mut c_void) { + use x11rb::connection::Connection as _; + use x11rb::protocol::xproto::ConnectionExt as _; + + if let Ok(xcb) = unsafe { + x11rb::xcb_ffi::XCBConnection::from_raw_xcb_connection( + connection.as_ptr(), + false, + ) + } { + let _ = xcb.destroy_window(*window_id); + let _ = xcb.flush(); + } + } + } + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "wayland"))] + Self::Wayland { .. } => {} + } + } +} + +unsafe impl Send for NativeSurfaceHandle {} + +unsafe impl Sync for NativeSurfaceHandle {} + +impl HasWindowHandle for NativeSurfaceHandle { + fn window_handle(&self) -> Result, HandleError> { + match self.inner.as_ref() { + #[cfg(target_os = "macos")] + NativeSurfaceHandleInner::MacOS { appkit_ns_view } => unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::AppKit( + raw_window_handle::AppKitWindowHandle::new(*appkit_ns_view), + ), + )) + }, + #[cfg(target_os = "windows")] + NativeSurfaceHandleInner::Windows { hwnd } => { + let Some(hwnd) = NonZeroIsize::new(*hwnd) else { + return Err(HandleError::Unavailable); + }; + let handle = raw_window_handle::Win32WindowHandle::new(hwnd); + unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::Win32(handle), + )) + } + } + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + NativeSurfaceHandleInner::X11 { window_id, visual_id, .. } => { + let Some(window_id) = std::num::NonZeroU32::new(*window_id) else { + return Err(HandleError::Unavailable); + }; + let mut handle = raw_window_handle::XcbWindowHandle::new(window_id); + handle.visual_id = std::num::NonZeroU32::new(*visual_id); + unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::Xcb(handle), + )) + } + } + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "wayland"))] + NativeSurfaceHandleInner::Wayland { surface, .. } => { + let Some(surface) = NonNull::new(*surface as *mut c_void) else { + return Err(HandleError::Unavailable); + }; + let handle = raw_window_handle::WaylandWindowHandle::new(surface); + unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::Wayland(handle), + )) + } + } + } + } +} + +impl HasDisplayHandle for NativeSurfaceHandle { + fn display_handle(&self) -> Result, HandleError> { + match self.inner.as_ref() { + #[cfg(target_os = "macos")] + NativeSurfaceHandleInner::MacOS { .. } => unsafe { + Ok(raw_window_handle::DisplayHandle::borrow_raw( + raw_window_handle::AppKitDisplayHandle::new().into(), + )) + }, + #[cfg(target_os = "windows")] + NativeSurfaceHandleInner::Windows { .. } => Ok(raw_window_handle::DisplayHandle::windows()), + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "x11"))] + NativeSurfaceHandleInner::X11 { connection, screen_id, .. } => { + let Some(connection) = NonNull::new(*connection as *mut c_void) else { + return Err(HandleError::Unavailable); + }; + let handle = raw_window_handle::XcbDisplayHandle::new(Some(connection), *screen_id); + unsafe { + Ok(raw_window_handle::DisplayHandle::borrow_raw( + raw_window_handle::RawDisplayHandle::Xcb(handle), + )) + } + } + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "wayland"))] + NativeSurfaceHandleInner::Wayland { display, .. } => { + let Some(display) = NonNull::new(*display as *mut c_void) else { + return Err(HandleError::Unavailable); + }; + let handle = raw_window_handle::WaylandDisplayHandle::new(display); + unsafe { + Ok(raw_window_handle::DisplayHandle::borrow_raw( + raw_window_handle::RawDisplayHandle::Wayland(handle), + )) + } + } + } + } +} + pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn bounds(&self) -> Bounds; fn is_maximized(&self) -> bool; @@ -500,6 +744,10 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn draw(&self, scene: &Scene); fn completed_frame(&self) {} fn sprite_atlas(&self) -> Arc; + fn create_native_surface(&self) -> Option { + None + } + fn sync_native_surface(&self, _surface: &NativeSurfaceHandle, _bounds: Bounds) {} // macOS specific methods fn get_title(&self) -> String { diff --git a/third_party/gpui/src/platform/linux/wayland/client.rs b/third_party/gpui/src/platform/linux/wayland/client.rs index 1ebdda3..a762201 100644 --- a/third_party/gpui/src/platform/linux/wayland/client.rs +++ b/third_party/gpui/src/platform/linux/wayland/client.rs @@ -33,7 +33,7 @@ use wayland_client::{ Connection, Dispatch, Proxy, QueueHandle, delegate_noop, protocol::{ wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm, - wl_shm_pool, wl_surface, + wl_shm_pool, wl_subcompositor, wl_subsurface, wl_surface, }, }; use wayland_protocols::wp::cursor_shape::v1::client::{ @@ -111,6 +111,7 @@ pub struct Globals { pub wm_base: xdg_wm_base::XdgWmBase, pub shm: wl_shm::WlShm, pub seat: wl_seat::WlSeat, + pub subcompositor: Option, pub viewporter: Option, pub fractional_scale_manager: Option, @@ -149,6 +150,7 @@ impl Globals { shm: globals.bind(&qh, 1..=1, ()).unwrap(), seat, wm_base: globals.bind(&qh, 2..=5, ()).unwrap(), + subcompositor: globals.bind(&qh, 1..=1, ()).ok(), viewporter: globals.bind(&qh, 1..=1, ()).ok(), fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(), decoration_manager: globals.bind(&qh, 1..=1, ()).ok(), @@ -943,6 +945,8 @@ delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm); delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool); delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer); delegate_noop!(WaylandClientStatePtr: ignore wl_region::WlRegion); +delegate_noop!(WaylandClientStatePtr: wl_subcompositor::WlSubcompositor); +delegate_noop!(WaylandClientStatePtr: wl_subsurface::WlSubsurface); delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1); delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1); delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur_manager::OrgKdeKwinBlurManager); diff --git a/third_party/gpui/src/platform/linux/wayland/window.rs b/third_party/gpui/src/platform/linux/wayland/window.rs index aa3b714..0ed2112 100644 --- a/third_party/gpui/src/platform/linux/wayland/window.rs +++ b/third_party/gpui/src/platform/linux/wayland/window.rs @@ -13,7 +13,10 @@ use futures::channel::oneshot::Receiver; use raw_window_handle as rwh; use wayland_backend::client::ObjectId; use wayland_client::WEnum; -use wayland_client::{Proxy, protocol::wl_surface}; +use wayland_client::{ + Proxy, + protocol::{wl_subsurface, wl_surface}, +}; use wayland_protocols::wp::viewporter::client::wp_viewport; use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1; use wayland_protocols::xdg::shell::client::xdg_surface; @@ -92,6 +95,7 @@ pub struct WaylandWindowState { blur: Option, toplevel: xdg_toplevel::XdgToplevel, viewport: Option, + native_surfaces: HashMap, outputs: HashMap, display: Option<(ObjectId, Output)>, globals: Globals, @@ -166,6 +170,7 @@ impl WaylandWindowState { blur: None, toplevel, viewport, + native_surfaces: HashMap::default(), globals, outputs: HashMap::default(), display: None, @@ -222,6 +227,22 @@ impl WaylandWindowState { } } +struct WaylandNativeSurface { + surface: wl_surface::WlSurface, + subsurface: wl_subsurface::WlSubsurface, + viewport: Option, +} + +impl WaylandNativeSurface { + fn destroy(self) { + if let Some(viewport) = self.viewport { + viewport.destroy(); + } + self.subsurface.destroy(); + self.surface.destroy(); + } +} + pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr); pub enum ImeInput { InsertText(String), @@ -247,6 +268,9 @@ impl Drop for WaylandWindow { if let Some(viewport) = &state.viewport { viewport.destroy(); } + for (_, native_surface) in state.native_surfaces.drain() { + native_surface.destroy(); + } state.xdg_surface.destroy(); state.surface.destroy(); @@ -1032,6 +1056,52 @@ impl PlatformWindow for WaylandWindow { state.renderer.sprite_atlas().clone() } + fn create_native_surface(&self) -> Option { + let mut state = self.borrow_mut(); + let subcompositor = state.globals.subcompositor.as_ref()?; + let surface = state.globals.compositor.create_surface(&state.globals.qh, ()); + let subsurface = + subcompositor.get_subsurface(&surface, &state.surface, &state.globals.qh, ()); + subsurface.set_desync(); + let viewport = state + .globals + .viewporter + .as_ref() + .map(|viewporter| viewporter.get_viewport(&surface, &state.globals.qh, ())); + let display = surface.backend().upgrade()?.display_ptr().cast::() as usize; + let surface_id = surface.id().as_ptr().cast::() as usize; + surface.commit(); + state.native_surfaces.insert( + surface_id, + WaylandNativeSurface { + surface, + subsurface, + viewport, + }, + ); + Some(crate::NativeSurfaceHandle::from_wayland_surface( + surface_id, display, + )) + } + + fn sync_native_surface(&self, surface: &crate::NativeSurfaceHandle, bounds: Bounds) { + let state = self.borrow(); + let bounds = bounds.to_device_pixels(state.scale); + if let Some(native_surface) = state.native_surfaces.get(&surface.identity()) { + native_surface + .subsurface + .set_position(bounds.origin.x.0, bounds.origin.y.0); + if let Some(viewport) = native_surface.viewport.as_ref() { + viewport.set_destination( + bounds.size.width.0.max(1), + bounds.size.height.0.max(1), + ); + } + native_surface.surface.commit(); + state.surface.commit(); + } + } + fn show_window_menu(&self, position: Point) { let state = self.borrow(); let serial = state.client.get_serial(SerialKind::MousePress); diff --git a/third_party/gpui/src/platform/linux/x11/window.rs b/third_party/gpui/src/platform/linux/x11/window.rs index fe197a6..0cc1e61 100644 --- a/third_party/gpui/src/platform/linux/x11/window.rs +++ b/third_party/gpui/src/platform/linux/x11/window.rs @@ -4,10 +4,11 @@ use x11rb::connection::RequestConnection; use crate::platform::blade::{BladeContext, BladeRenderer, BladeSurfaceConfig}; use crate::{ AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GpuSpecs, Modifiers, - Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, - Point, PromptButton, PromptLevel, RequestFrameOptions, ResizeEdge, ScaledPixels, Scene, Size, - Tiling, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, - WindowDecorations, WindowKind, WindowParams, X11ClientStatePtr, px, size, + NativeSurfaceHandle, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, + PlatformInputHandler, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, + ResizeEdge, ScaledPixels, Scene, Size, Tiling, WindowAppearance, WindowBackgroundAppearance, + WindowBounds, WindowControlArea, WindowDecorations, WindowKind, WindowParams, + X11ClientStatePtr, px, size, }; use blade_graphics as gpu; @@ -253,6 +254,8 @@ pub struct X11WindowState { executor: ForegroundExecutor, atoms: XcbAtoms, x_root_window: xproto::Window, + x_screen_id: usize, + x_visual_id: u32, pub(crate) counter_id: sync::Counter, pub(crate) last_sync_counter: Option, bounds: Bounds, @@ -671,6 +674,8 @@ impl X11WindowState { executor, display, x_root_window: visual_set.root, + x_screen_id: x_screen_index, + x_visual_id: visual.id, bounds: bounds.to_pixels(scale_factor), scale_factor, renderer, @@ -1480,6 +1485,61 @@ impl PlatformWindow for X11Window { inner.renderer.sprite_atlas().clone() } + fn create_native_surface(&self) -> Option { + let state = self.0.state.borrow(); + let window_id = self.0.xcb.generate_id().log_err()?; + let aux = xproto::CreateWindowAux::new().event_mask(xproto::EventMask::NO_EVENT); + check_reply( + || "X11 CreateWindow failed for native surface.", + self.0.xcb.create_window( + 0, + window_id, + self.0.x_window, + 0, + 0, + 1, + 1, + 0, + xproto::WindowClass::INPUT_OUTPUT, + 0, + &aux, + ), + ) + .log_err()?; + check_reply( + || "X11 MapWindow failed for native surface.", + self.0.xcb.map_window(window_id), + ) + .log_err()?; + xcb_flush(&self.0.xcb); + Some(NativeSurfaceHandle::from_xcb_window( + as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(&*self.0.xcb) + as usize, + state.x_screen_id as i32, + window_id, + state.x_visual_id, + )) + } + + fn sync_native_surface(&self, surface: &NativeSurfaceHandle, bounds: Bounds) { + let state = self.0.state.borrow(); + let bounds = bounds.to_device_pixels(state.scale_factor); + drop(state); + check_reply( + || "X11 ConfigureWindow failed for native surface.", + self.0.xcb.configure_window( + surface.xcb_window_id(), + &xproto::ConfigureWindowAux::new() + .x(bounds.origin.x.0) + .y(bounds.origin.y.0) + .width(bounds.size.width.0.max(1) as u32) + .height(bounds.size.height.0.max(1) as u32), + ), + ) + .log_err(); + xcb_flush(&self.0.xcb); + } + fn show_window_menu(&self, position: Point) { let state = self.0.state.borrow(); diff --git a/third_party/gpui/src/platform/mac/window.rs b/third_party/gpui/src/platform/mac/window.rs index 708783f..c2f31af 100644 --- a/third_party/gpui/src/platform/mac/window.rs +++ b/third_party/gpui/src/platform/mac/window.rs @@ -2,11 +2,12 @@ use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer}; use crate::{ AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, - PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, - SharedString, Size, SystemWindowTab, Timer, WindowAppearance, WindowBackgroundAppearance, - WindowBounds, WindowControlArea, WindowKind, WindowParams, dispatch_get_main_queue, - dispatch_sys::dispatch_async_f, platform::PlatformInputHandler, point, px, size, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, NativeSurfaceHandle, Pixels, PlatformAtlas, + PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, + RequestFrameOptions, SharedString, Size, SystemWindowTab, Timer, WindowAppearance, + WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowKind, WindowParams, + dispatch_get_main_queue, dispatch_sys::dispatch_async_f, platform::PlatformInputHandler, point, + px, size, }; use block::ConcreteBlock; use cocoa::{ @@ -57,6 +58,7 @@ const WINDOW_STATE_IVAR: &str = "windowState"; static mut WINDOW_CLASS: *const Class = ptr::null(); static mut PANEL_CLASS: *const Class = ptr::null(); static mut VIEW_CLASS: *const Class = ptr::null(); +static mut NATIVE_SURFACE_VIEW_CLASS: *const Class = ptr::null(); static mut BLURRED_VIEW_CLASS: *const Class = ptr::null(); #[allow(non_upper_case_globals)] @@ -252,6 +254,16 @@ unsafe fn build_classes() { } decl.register() }; + NATIVE_SURFACE_VIEW_CLASS = { + let mut decl = ClassDecl::new("GPUINativeSurfaceView", class!(NSView)).unwrap(); + unsafe { + decl.add_method( + sel!(hitTest:), + native_surface_hit_test as extern "C" fn(&Object, Sel, NSPoint) -> id, + ); + decl.register() + } + }; BLURRED_VIEW_CLASS = { let mut decl = ClassDecl::new("BlurredView", class!(NSVisualEffectView)).unwrap(); unsafe { @@ -1504,6 +1516,45 @@ impl PlatformWindow for MacWindow { self.0.lock().renderer.sprite_atlas().clone() } + fn create_native_surface(&self) -> Option { + let this = self.0.lock(); + unsafe { + let parent = this.native_view.as_ptr() as id; + let native_surface: id = msg_send![NATIVE_SURFACE_VIEW_CLASS, alloc]; + let native_surface = NSView::initWithFrame_( + native_surface, + NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(1.0, 1.0)), + ); + if native_surface.is_null() { + return None; + } + native_surface.setWantsBestResolutionOpenGLSurface_(YES); + native_surface.setWantsLayer(YES); + let _: () = msg_send![ + native_surface, + setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize + ]; + let _: () = msg_send![parent, addSubview: native_surface]; + NonNull::new(native_surface as *mut c_void).map(NativeSurfaceHandle::from_appkit_ns_view) + } + } + + fn sync_native_surface(&self, surface: &NativeSurfaceHandle, bounds: Bounds) { + let this = self.0.lock(); + unsafe { + let parent = this.native_view.as_ptr() as id; + let parent_frame: NSRect = msg_send![parent, frame]; + let view = surface.appkit_ns_view().as_ptr() as id; + let width = bounds.size.width.0.max(1.0) as f64; + let height = bounds.size.height.0.max(1.0) as f64; + let x = bounds.origin.x.0 as f64; + let y = (parent_frame.size.height - bounds.origin.y.0 as f64 - height).max(0.0); + let frame = NSRect::new(NSPoint::new(x, y), NSSize::new(width, height)); + let _: () = msg_send![view, setFrame: frame]; + let _: () = msg_send![view, setHidden: NO]; + } + } + fn gpu_specs(&self) -> Option { None } @@ -1632,6 +1683,10 @@ extern "C" fn yes(_: &Object, _: Sel) -> BOOL { YES } +extern "C" fn native_surface_hit_test(_: &Object, _: Sel, _: NSPoint) -> id { + nil +} + extern "C" fn dealloc_window(this: &Object, _: Sel) { unsafe { drop_window_state(this); diff --git a/third_party/gpui/src/platform/windows/window.rs b/third_party/gpui/src/platform/windows/window.rs index e765fa1..c1d339a 100644 --- a/third_party/gpui/src/platform/windows/window.rs +++ b/third_party/gpui/src/platform/windows/window.rs @@ -849,6 +849,46 @@ impl PlatformWindow for WindowsWindow { self.0.state.borrow().renderer.sprite_atlas() } + fn create_native_surface(&self) -> Option { + register_native_surface_window_class(); + let hwnd = unsafe { + CreateWindowExW( + WS_EX_NOACTIVATE, + NATIVE_SURFACE_CLASS_NAME, + w!(""), + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED, + 0, + 0, + 1, + 1, + Some(self.0.hwnd), + None, + Some(get_module_handle().into()), + None, + ) + } + .ok()?; + Some(NativeSurfaceHandle::from_win32_hwnd(hwnd.0 as isize)) + } + + fn sync_native_surface(&self, surface: &NativeSurfaceHandle, bounds: Bounds) { + let bounds = bounds.to_device_pixels(self.scale_factor()); + let hwnd = HWND(surface.win32_hwnd() as _); + unsafe { + SetWindowPos( + hwnd, + None, + bounds.origin.x.0, + bounds.origin.y.0, + bounds.size.width.0.max(1), + bounds.size.height.0.max(1), + SWP_NOZORDER | SWP_NOACTIVATE, + ) + .log_err(); + ShowWindow(hwnd, SW_SHOW).ok().log_err(); + } + } + fn get_raw_handle(&self) -> HWND { self.0.hwnd } @@ -1145,6 +1185,7 @@ enum WindowOpenState { } const WINDOW_CLASS_NAME: PCWSTR = w!("Zed::Window"); +const NATIVE_SURFACE_CLASS_NAME: PCWSTR = w!("Zed::NativeSurface"); fn register_window_class(icon_handle: HICON) { static ONCE: Once = Once::new(); @@ -1162,6 +1203,30 @@ fn register_window_class(icon_handle: HICON) { }); } +fn register_native_surface_window_class() { + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + let wc = WNDCLASSW { + lpfnWndProc: Some(native_surface_window_procedure), + lpszClassName: PCWSTR(NATIVE_SURFACE_CLASS_NAME.as_ptr()), + style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW, + hInstance: get_module_handle().into(), + hbrBackground: unsafe { CreateSolidBrush(COLORREF(0x00000000)) }, + ..Default::default() + }; + unsafe { RegisterClassW(&wc) }; + }); +} + +unsafe extern "system" fn native_surface_window_procedure( + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT { + unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } +} + unsafe extern "system" fn window_procedure( hwnd: HWND, msg: u32, diff --git a/third_party/gpui/src/window.rs b/third_party/gpui/src/window.rs index 92835a1..e55f2c3 100644 --- a/third_party/gpui/src/window.rs +++ b/third_party/gpui/src/window.rs @@ -10,13 +10,14 @@ use crate::{ LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad, - Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, - SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, ScaledPixels, Scene, Shadow, - SharedString, Size, StrikethroughStyle, Style, SubscriberSet, Subscription, SystemWindowTab, - SystemWindowTabController, TabStopMap, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, - TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, - WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, - point, prelude::*, px, rems, size, transparent_black, + NativeSurfaceHandle, Render, RenderGlyphParams, RenderImage, RenderImageParams, + RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, + SUBPIXEL_VARIANTS_Y, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, + Style, SubscriberSet, Subscription, SystemWindowTab, SystemWindowTabController, TabStopMap, + TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix, Underline, + UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, + WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, point, prelude::*, px, rems, + size, transparent_black, }; use anyhow::{Context as _, Result, anyhow}; use collections::{FxHashMap, FxHashSet}; @@ -3201,6 +3202,16 @@ impl Window { }); } + /// Creates a native child surface hosted by this window. + pub fn create_native_surface(&self) -> Option { + self.platform_window.create_native_surface() + } + + /// Synchronizes a native child surface to a GPUI layout box. + pub fn sync_native_surface(&self, surface: &NativeSurfaceHandle, bounds: Bounds) { + self.platform_window.sync_native_surface(surface, bounds); + } + /// Removes an image from the sprite atlas. pub fn drop_image(&mut self, data: Arc) -> Result<()> { for frame_index in 0..data.frame_count() { diff --git a/third_party/gpui_support/gpui_http_client/Cargo.toml b/third_party/gpui_support/gpui_http_client/Cargo.toml new file mode 100644 index 0000000..0a3efdb --- /dev/null +++ b/third_party/gpui_support/gpui_http_client/Cargo.toml @@ -0,0 +1,133 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# Local copy keeps the registry manifest shape and narrows log features so +# GPUI and Servo can share the same workspace dependency graph. + +[package] +edition = "2024" +name = "gpui_http_client" +version = "0.2.2" +build = false +publish = true +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A HTTP client library for Zed and GPUI" +readme = false +license = "Apache-2.0" +resolver = "2" + +[features] +test-support = [] + +[lib] +name = "gpui_http_client" +path = "src/http_client.rs" +doctest = true + +[dependencies.anyhow] +version = "1.0.86" + +[dependencies.async-compression] +version = "0.4" +features = [ + "gzip", + "futures-io", +] + +[dependencies.async-fs] +version = "2.1" + +[dependencies.async-tar] +version = "0.5.0-zed" +package = "zed-async-tar" + +[dependencies.bytes] +version = "1.0" + +[dependencies.derive_more] +version = "0.99.17" + +[dependencies.futures] +version = "0.3" + +[dependencies.http] +version = "1.1" + +[dependencies.http-body] +version = "1.0" + +[dependencies.log] +version = "0.4.16" + +[dependencies.parking_lot] +version = "0.12.1" + +[dependencies.reqwest] +version = "0.12.15-zed" +features = [ + "charset", + "http2", + "macos-system-configuration", + "multipart", + "rustls-tls-native-roots", + "socks", + "stream", +] +default-features = false +package = "zed-reqwest" + +[dependencies.serde] +version = "1.0.221" +features = [ + "derive", + "rc", +] + +[dependencies.serde_json] +version = "1.0.144" +features = [ + "preserve_order", + "raw_value", +] + +[dependencies.sha2] +version = "0.10" + +[dependencies.tempfile] +version = "3.20.0" + +[dependencies.url] +version = "2.2" + +[dependencies.util] +version = "0.2.2" +package = "gpui_util" + +[lints.clippy] +dbg_macro = "deny" +declare_interior_mutable_const = "deny" +disallowed_methods = "deny" +large_enum_variant = "allow" +let_underscore_future = "allow" +nonminimal_bool = "allow" +redundant_clone = "deny" +single_range_in_vec_init = "allow" +todo = "deny" +too_many_arguments = "allow" +type_complexity = "allow" + +[lints.clippy.style] +level = "allow" +priority = -1 + +[lints.rust.unexpected_cfgs] +level = "allow" +priority = 0 diff --git a/third_party/gpui_support/gpui_http_client/LICENSE-APACHE b/third_party/gpui_support/gpui_http_client/LICENSE-APACHE new file mode 100644 index 0000000..461a0fe --- /dev/null +++ b/third_party/gpui_support/gpui_http_client/LICENSE-APACHE @@ -0,0 +1,222 @@ +Copyright 2022 - 2025 Zed Industries, Inc. + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + + 1. Definitions. + + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + END OF TERMS AND CONDITIONS diff --git a/third_party/gpui_support/gpui_http_client/src/async_body.rs b/third_party/gpui_support/gpui_http_client/src/async_body.rs new file mode 100644 index 0000000..6b99a54 --- /dev/null +++ b/third_party/gpui_support/gpui_http_client/src/async_body.rs @@ -0,0 +1,149 @@ +use std::{ + io::{Cursor, Read}, + pin::Pin, + task::Poll, +}; + +use bytes::Bytes; +use futures::AsyncRead; +use http_body::{Body, Frame}; + +/// Based on the implementation of AsyncBody in +/// . +pub struct AsyncBody(pub Inner); + +pub enum Inner { + /// An empty body. + Empty, + + /// A body stored in memory. + Bytes(std::io::Cursor), + + /// An asynchronous reader. + AsyncReader(Pin>), +} + +impl AsyncBody { + /// Create a new empty body. + /// + /// An empty body represents the *absence* of a body, which is semantically + /// different than the presence of a body of zero length. + pub fn empty() -> Self { + Self(Inner::Empty) + } + /// Create a streaming body that reads from the given reader. + pub fn from_reader(read: R) -> Self + where + R: AsyncRead + Send + Sync + 'static, + { + Self(Inner::AsyncReader(Box::pin(read))) + } + + pub fn from_bytes(bytes: Bytes) -> Self { + Self(Inner::Bytes(Cursor::new(bytes))) + } +} + +impl Default for AsyncBody { + fn default() -> Self { + Self(Inner::Empty) + } +} + +impl From<()> for AsyncBody { + fn from(_: ()) -> Self { + Self(Inner::Empty) + } +} + +impl From for AsyncBody { + fn from(bytes: Bytes) -> Self { + Self::from_bytes(bytes) + } +} + +impl From> for AsyncBody { + fn from(body: Vec) -> Self { + Self::from_bytes(body.into()) + } +} + +impl From for AsyncBody { + fn from(body: String) -> Self { + Self::from_bytes(body.into()) + } +} + +impl From<&'static [u8]> for AsyncBody { + #[inline] + fn from(s: &'static [u8]) -> Self { + Self::from_bytes(Bytes::from_static(s)) + } +} + +impl From<&'static str> for AsyncBody { + #[inline] + fn from(s: &'static str) -> Self { + Self::from_bytes(Bytes::from_static(s.as_bytes())) + } +} + +impl TryFrom for AsyncBody { + type Error = anyhow::Error; + + fn try_from(value: reqwest::Body) -> Result { + value + .as_bytes() + .ok_or_else(|| anyhow::anyhow!("Underlying data is a stream")) + .map(|bytes| Self::from_bytes(Bytes::copy_from_slice(bytes))) + } +} + +impl> From> for AsyncBody { + fn from(body: Option) -> Self { + match body { + Some(body) => body.into(), + None => Self::empty(), + } + } +} + +impl futures::AsyncRead for AsyncBody { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + // SAFETY: Standard Enum pin projection + let inner = unsafe { &mut self.get_unchecked_mut().0 }; + match inner { + Inner::Empty => Poll::Ready(Ok(0)), + // Blocking call is over an in-memory buffer + Inner::Bytes(cursor) => Poll::Ready(cursor.read(buf)), + Inner::AsyncReader(async_reader) => { + AsyncRead::poll_read(async_reader.as_mut(), cx, buf) + } + } + } +} + +impl Body for AsyncBody { + type Data = Bytes; + type Error = std::io::Error; + + fn poll_frame( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll, Self::Error>>> { + let mut buffer = vec![0; 8192]; + match AsyncRead::poll_read(self.as_mut(), cx, &mut buffer) { + Poll::Ready(Ok(0)) => Poll::Ready(None), + Poll::Ready(Ok(n)) => { + let data = Bytes::copy_from_slice(&buffer[..n]); + Poll::Ready(Some(Ok(Frame::data(data)))) + } + Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/third_party/gpui_support/gpui_http_client/src/github.rs b/third_party/gpui_support/gpui_http_client/src/github.rs new file mode 100644 index 0000000..32efed8 --- /dev/null +++ b/third_party/gpui_support/gpui_http_client/src/github.rs @@ -0,0 +1,179 @@ +use crate::HttpClient; +use anyhow::{Context as _, Result, anyhow, bail}; +use futures::AsyncReadExt; +use serde::Deserialize; +use std::sync::Arc; +use url::Url; + +pub struct GitHubLspBinaryVersion { + pub name: String, + pub url: String, + pub digest: Option, +} + +#[derive(Deserialize, Debug)] +pub struct GithubRelease { + pub tag_name: String, + #[serde(rename = "prerelease")] + pub pre_release: bool, + pub assets: Vec, + pub tarball_url: String, + pub zipball_url: String, +} + +#[derive(Deserialize, Debug)] +pub struct GithubReleaseAsset { + pub name: String, + pub browser_download_url: String, + pub digest: Option, +} + +pub async fn latest_github_release( + repo_name_with_owner: &str, + require_assets: bool, + pre_release: bool, + http: Arc, +) -> anyhow::Result { + let mut response = http + .get( + format!("https://api.github.com/repos/{repo_name_with_owner}/releases").as_str(), + Default::default(), + true, + ) + .await + .context("error fetching latest release")?; + + let mut body = Vec::new(); + response + .body_mut() + .read_to_end(&mut body) + .await + .context("error reading latest release")?; + + if response.status().is_client_error() { + let text = String::from_utf8_lossy(body.as_slice()); + bail!( + "status error {}, response: {text:?}", + response.status().as_u16() + ); + } + + let releases = match serde_json::from_slice::>(body.as_slice()) { + Ok(releases) => releases, + + Err(err) => { + log::error!("Error deserializing: {err:?}"); + log::error!( + "GitHub API response text: {:?}", + String::from_utf8_lossy(body.as_slice()) + ); + anyhow::bail!("error deserializing latest release: {err:?}"); + } + }; + + let mut release = releases + .into_iter() + .filter(|release| !require_assets || !release.assets.is_empty()) + .find(|release| release.pre_release == pre_release) + .context("finding a prerelease")?; + release.assets.iter_mut().for_each(|asset| { + if let Some(digest) = &mut asset.digest + && let Some(stripped) = digest.strip_prefix("sha256:") + { + *digest = stripped.to_owned(); + } + }); + Ok(release) +} + +pub async fn get_release_by_tag_name( + repo_name_with_owner: &str, + tag: &str, + http: Arc, +) -> anyhow::Result { + let mut response = http + .get( + &format!("https://api.github.com/repos/{repo_name_with_owner}/releases/tags/{tag}"), + Default::default(), + true, + ) + .await + .context("error fetching latest release")?; + + let mut body = Vec::new(); + let status = response.status(); + response + .body_mut() + .read_to_end(&mut body) + .await + .context("error reading latest release")?; + + if status.is_client_error() { + let text = String::from_utf8_lossy(body.as_slice()); + bail!( + "status error {}, response: {text:?}", + response.status().as_u16() + ); + } + + let release = serde_json::from_slice::(body.as_slice()).map_err(|err| { + log::error!("Error deserializing: {err:?}"); + log::error!( + "GitHub API response text: {:?}", + String::from_utf8_lossy(body.as_slice()) + ); + anyhow!("error deserializing GitHub release: {err:?}") + })?; + + Ok(release) +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum AssetKind { + TarGz, + Gz, + Zip, +} + +pub fn build_asset_url(repo_name_with_owner: &str, tag: &str, kind: AssetKind) -> Result { + let mut url = Url::parse(&format!( + "https://github.com/{repo_name_with_owner}/archive/refs/tags", + ))?; + // We're pushing this here, because tags may contain `/` and other characters + // that need to be escaped. + let asset_filename = format!( + "{tag}.{extension}", + extension = match kind { + AssetKind::TarGz => "tar.gz", + AssetKind::Gz => "gz", + AssetKind::Zip => "zip", + } + ); + url.path_segments_mut() + .map_err(|()| anyhow!("cannot modify url path segments"))? + .push(&asset_filename); + Ok(url.to_string()) +} + +#[cfg(test)] +mod tests { + use crate::github::{AssetKind, build_asset_url}; + + #[test] + fn test_build_asset_url() { + let tag = "release/2.3.5"; + let repo_name_with_owner = "microsoft/vscode-eslint"; + + let tarball = build_asset_url(repo_name_with_owner, tag, AssetKind::TarGz).unwrap(); + assert_eq!( + tarball, + "https://github.com/microsoft/vscode-eslint/archive/refs/tags/release%2F2.3.5.tar.gz" + ); + + let zip = build_asset_url(repo_name_with_owner, tag, AssetKind::Zip).unwrap(); + assert_eq!( + zip, + "https://github.com/microsoft/vscode-eslint/archive/refs/tags/release%2F2.3.5.zip" + ); + } +} diff --git a/third_party/gpui_support/gpui_http_client/src/github_download.rs b/third_party/gpui_support/gpui_http_client/src/github_download.rs new file mode 100644 index 0000000..02dee08 --- /dev/null +++ b/third_party/gpui_support/gpui_http_client/src/github_download.rs @@ -0,0 +1,189 @@ +use std::{path::Path, pin::Pin, task::Poll}; + +use anyhow::{Context, Result}; +use async_compression::futures::bufread::GzipDecoder; +use futures::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWrite, io::BufReader}; +use sha2::{Digest, Sha256}; + +use crate::{HttpClient, github::AssetKind}; + +#[derive(serde::Deserialize, serde::Serialize, Debug)] +pub struct GithubBinaryMetadata { + pub metadata_version: u64, + pub digest: Option, +} + +impl GithubBinaryMetadata { + pub async fn read_from_file(metadata_path: &Path) -> Result { + let metadata_content = async_fs::read_to_string(metadata_path) + .await + .with_context(|| format!("reading metadata file at {metadata_path:?}"))?; + serde_json::from_str(&metadata_content) + .with_context(|| format!("parsing metadata file at {metadata_path:?}")) + } + + pub async fn write_to_file(&self, metadata_path: &Path) -> Result<()> { + let metadata_content = serde_json::to_string(self) + .with_context(|| format!("serializing metadata for {metadata_path:?}"))?; + async_fs::write(metadata_path, metadata_content.as_bytes()) + .await + .with_context(|| format!("writing metadata file at {metadata_path:?}"))?; + Ok(()) + } +} + +pub async fn download_server_binary( + http_client: &dyn HttpClient, + url: &str, + digest: Option<&str>, + destination_path: &Path, + asset_kind: AssetKind, +) -> Result<(), anyhow::Error> { + log::info!("downloading github artifact from {url}"); + let mut response = http_client + .get(url, Default::default(), true) + .await + .with_context(|| format!("downloading release from {url}"))?; + let body = response.body_mut(); + match digest { + Some(expected_sha_256) => { + let temp_asset_file = tempfile::NamedTempFile::new() + .with_context(|| format!("creating a temporary file for {url}"))?; + let (temp_asset_file, _temp_guard) = temp_asset_file.into_parts(); + let mut writer = HashingWriter { + writer: async_fs::File::from(temp_asset_file), + hasher: Sha256::new(), + }; + futures::io::copy(&mut BufReader::new(body), &mut writer) + .await + .with_context(|| { + format!("saving archive contents into the temporary file for {url}",) + })?; + let asset_sha_256 = format!("{:x}", writer.hasher.finalize()); + + anyhow::ensure!( + asset_sha_256 == expected_sha_256, + "{url} asset got SHA-256 mismatch. Expected: {expected_sha_256}, Got: {asset_sha_256}", + ); + writer + .writer + .seek(std::io::SeekFrom::Start(0)) + .await + .with_context(|| format!("seeking temporary file {destination_path:?}",))?; + stream_file_archive(&mut writer.writer, url, destination_path, asset_kind) + .await + .with_context(|| { + format!("extracting downloaded asset for {url} into {destination_path:?}",) + })?; + } + None => stream_response_archive(body, url, destination_path, asset_kind) + .await + .with_context(|| { + format!("extracting response for asset {url} into {destination_path:?}",) + })?, + } + Ok(()) +} + +async fn stream_response_archive( + response: impl AsyncRead + Unpin, + url: &str, + destination_path: &Path, + asset_kind: AssetKind, +) -> Result<()> { + match asset_kind { + AssetKind::TarGz => extract_tar_gz(destination_path, url, response).await?, + AssetKind::Gz => extract_gz(destination_path, url, response).await?, + AssetKind::Zip => { + util::archive::extract_zip(destination_path, response).await?; + } + }; + Ok(()) +} + +async fn stream_file_archive( + file_archive: impl AsyncRead + AsyncSeek + Unpin, + url: &str, + destination_path: &Path, + asset_kind: AssetKind, +) -> Result<()> { + match asset_kind { + AssetKind::TarGz => extract_tar_gz(destination_path, url, file_archive).await?, + AssetKind::Gz => extract_gz(destination_path, url, file_archive).await?, + #[cfg(not(windows))] + AssetKind::Zip => { + util::archive::extract_seekable_zip(destination_path, file_archive).await?; + } + #[cfg(windows)] + AssetKind::Zip => { + util::archive::extract_zip(destination_path, file_archive).await?; + } + }; + Ok(()) +} + +async fn extract_tar_gz( + destination_path: &Path, + url: &str, + from: impl AsyncRead + Unpin, +) -> Result<(), anyhow::Error> { + let decompressed_bytes = GzipDecoder::new(BufReader::new(from)); + let archive = async_tar::Archive::new(decompressed_bytes); + archive + .unpack(&destination_path) + .await + .with_context(|| format!("extracting {url} to {destination_path:?}"))?; + Ok(()) +} + +async fn extract_gz( + destination_path: &Path, + url: &str, + from: impl AsyncRead + Unpin, +) -> Result<(), anyhow::Error> { + let mut decompressed_bytes = GzipDecoder::new(BufReader::new(from)); + let mut file = async_fs::File::create(&destination_path) + .await + .with_context(|| { + format!("creating a file {destination_path:?} for a download from {url}") + })?; + futures::io::copy(&mut decompressed_bytes, &mut file) + .await + .with_context(|| format!("extracting {url} to {destination_path:?}"))?; + Ok(()) +} + +struct HashingWriter { + writer: W, + hasher: Sha256, +} + +impl AsyncWrite for HashingWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> Poll> { + match Pin::new(&mut self.writer).poll_write(cx, buf) { + Poll::Ready(Ok(n)) => { + self.hasher.update(&buf[..n]); + Poll::Ready(Ok(n)) + } + other => other, + } + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + Pin::new(&mut self.writer).poll_flush(cx) + } + + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + Pin::new(&mut self.writer).poll_close(cx) + } +} diff --git a/third_party/gpui_support/gpui_http_client/src/http_client.rs b/third_party/gpui_support/gpui_http_client/src/http_client.rs new file mode 100644 index 0000000..056cee4 --- /dev/null +++ b/third_party/gpui_support/gpui_http_client/src/http_client.rs @@ -0,0 +1,481 @@ +mod async_body; +pub mod github; +pub mod github_download; + +pub use anyhow::{Result, anyhow}; +pub use async_body::{AsyncBody, Inner}; +use derive_more::Deref; +use http::HeaderValue; +pub use http::{self, Method, Request, Response, StatusCode, Uri, request::Builder}; + +use futures::{ + FutureExt as _, + future::{self, BoxFuture}, +}; +use parking_lot::Mutex; +#[cfg(feature = "test-support")] +use std::fmt; +use std::{any::type_name, sync::Arc}; +pub use url::Url; + +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] +pub enum RedirectPolicy { + #[default] + NoFollow, + FollowLimit(u32), + FollowAll, +} +pub struct FollowRedirects(pub bool); + +pub trait HttpRequestExt { + /// Conditionally modify self with the given closure. + fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self + where + Self: Sized, + { + if condition { then(self) } else { self } + } + + /// Conditionally unwrap and modify self with the given closure, if the given option is Some. + fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self + where + Self: Sized, + { + match option { + Some(value) => then(self, value), + None => self, + } + } + + /// Whether or not to follow redirects + fn follow_redirects(self, follow: RedirectPolicy) -> Self; +} + +impl HttpRequestExt for http::request::Builder { + fn follow_redirects(self, follow: RedirectPolicy) -> Self { + self.extension(follow) + } +} + +pub trait HttpClient: 'static + Send + Sync { + fn type_name(&self) -> &'static str; + + fn user_agent(&self) -> Option<&HeaderValue>; + + fn send( + &self, + req: http::Request, + ) -> BoxFuture<'static, anyhow::Result>>; + + fn get( + &self, + uri: &str, + body: AsyncBody, + follow_redirects: bool, + ) -> BoxFuture<'static, anyhow::Result>> { + let request = Builder::new() + .uri(uri) + .follow_redirects(if follow_redirects { + RedirectPolicy::FollowAll + } else { + RedirectPolicy::NoFollow + }) + .body(body); + + match request { + Ok(request) => self.send(request), + Err(e) => Box::pin(async move { Err(e.into()) }), + } + } + + fn post_json( + &self, + uri: &str, + body: AsyncBody, + ) -> BoxFuture<'static, anyhow::Result>> { + let request = Builder::new() + .uri(uri) + .method(Method::POST) + .header("Content-Type", "application/json") + .body(body); + + match request { + Ok(request) => self.send(request), + Err(e) => Box::pin(async move { Err(e.into()) }), + } + } + + fn proxy(&self) -> Option<&Url>; + + #[cfg(feature = "test-support")] + fn as_fake(&self) -> &FakeHttpClient { + panic!("called as_fake on {}", type_name::()) + } + + fn send_multipart_form<'a>( + &'a self, + _url: &str, + _request: reqwest::multipart::Form, + ) -> BoxFuture<'a, anyhow::Result>> { + future::ready(Err(anyhow!("not implemented"))).boxed() + } +} + +/// An [`HttpClient`] that may have a proxy. +#[derive(Deref)] +pub struct HttpClientWithProxy { + #[deref] + client: Arc, + proxy: Option, +} + +impl HttpClientWithProxy { + /// Returns a new [`HttpClientWithProxy`] with the given proxy URL. + pub fn new(client: Arc, proxy_url: Option) -> Self { + let proxy_url = proxy_url + .and_then(|proxy| proxy.parse().ok()) + .or_else(read_proxy_from_env); + + Self::new_url(client, proxy_url) + } + pub fn new_url(client: Arc, proxy_url: Option) -> Self { + Self { + client, + proxy: proxy_url, + } + } +} + +impl HttpClient for HttpClientWithProxy { + fn send( + &self, + req: Request, + ) -> BoxFuture<'static, anyhow::Result>> { + self.client.send(req) + } + + fn user_agent(&self) -> Option<&HeaderValue> { + self.client.user_agent() + } + + fn proxy(&self) -> Option<&Url> { + self.proxy.as_ref() + } + + fn type_name(&self) -> &'static str { + self.client.type_name() + } + + #[cfg(feature = "test-support")] + fn as_fake(&self) -> &FakeHttpClient { + self.client.as_fake() + } + + fn send_multipart_form<'a>( + &'a self, + url: &str, + form: reqwest::multipart::Form, + ) -> BoxFuture<'a, anyhow::Result>> { + self.client.send_multipart_form(url, form) + } +} + +/// An [`HttpClient`] that has a base URL. +pub struct HttpClientWithUrl { + base_url: Mutex, + client: HttpClientWithProxy, +} + +impl std::ops::Deref for HttpClientWithUrl { + type Target = HttpClientWithProxy; + + fn deref(&self) -> &Self::Target { + &self.client + } +} + +impl HttpClientWithUrl { + /// Returns a new [`HttpClientWithUrl`] with the given base URL. + pub fn new( + client: Arc, + base_url: impl Into, + proxy_url: Option, + ) -> Self { + let client = HttpClientWithProxy::new(client, proxy_url); + + Self { + base_url: Mutex::new(base_url.into()), + client, + } + } + + pub fn new_url( + client: Arc, + base_url: impl Into, + proxy_url: Option, + ) -> Self { + let client = HttpClientWithProxy::new_url(client, proxy_url); + + Self { + base_url: Mutex::new(base_url.into()), + client, + } + } + + /// Returns the base URL. + pub fn base_url(&self) -> String { + self.base_url.lock().clone() + } + + /// Sets the base URL. + pub fn set_base_url(&self, base_url: impl Into) { + let base_url = base_url.into(); + *self.base_url.lock() = base_url; + } + + /// Builds a URL using the given path. + pub fn build_url(&self, path: &str) -> String { + format!("{}{}", self.base_url(), path) + } + + /// Builds a Zed API URL using the given path. + pub fn build_zed_api_url(&self, path: &str, query: &[(&str, &str)]) -> Result { + let base_url = self.base_url(); + let base_api_url = match base_url.as_ref() { + "https://zed.dev" => "https://api.zed.dev", + "https://staging.zed.dev" => "https://api-staging.zed.dev", + "http://localhost:3000" => "http://localhost:8080", + other => other, + }; + + Ok(Url::parse_with_params( + &format!("{}{}", base_api_url, path), + query, + )?) + } + + /// Builds a Zed Cloud URL using the given path. + pub fn build_zed_cloud_url(&self, path: &str, query: &[(&str, &str)]) -> Result { + let base_url = self.base_url(); + let base_api_url = match base_url.as_ref() { + "https://zed.dev" => "https://cloud.zed.dev", + "https://staging.zed.dev" => "https://cloud.zed.dev", + "http://localhost:3000" => "http://localhost:8787", + other => other, + }; + + Ok(Url::parse_with_params( + &format!("{}{}", base_api_url, path), + query, + )?) + } + + /// Builds a Zed LLM URL using the given path. + pub fn build_zed_llm_url(&self, path: &str, query: &[(&str, &str)]) -> Result { + let base_url = self.base_url(); + let base_api_url = match base_url.as_ref() { + "https://zed.dev" => "https://cloud.zed.dev", + "https://staging.zed.dev" => "https://llm-staging.zed.dev", + "http://localhost:3000" => "http://localhost:8787", + other => other, + }; + + Ok(Url::parse_with_params( + &format!("{}{}", base_api_url, path), + query, + )?) + } +} + +impl HttpClient for HttpClientWithUrl { + fn send( + &self, + req: Request, + ) -> BoxFuture<'static, anyhow::Result>> { + self.client.send(req) + } + + fn user_agent(&self) -> Option<&HeaderValue> { + self.client.user_agent() + } + + fn proxy(&self) -> Option<&Url> { + self.client.proxy.as_ref() + } + + fn type_name(&self) -> &'static str { + self.client.type_name() + } + + #[cfg(feature = "test-support")] + fn as_fake(&self) -> &FakeHttpClient { + self.client.as_fake() + } + + fn send_multipart_form<'a>( + &'a self, + url: &str, + request: reqwest::multipart::Form, + ) -> BoxFuture<'a, anyhow::Result>> { + self.client.send_multipart_form(url, request) + } +} + +pub fn read_proxy_from_env() -> Option { + const ENV_VARS: &[&str] = &[ + "ALL_PROXY", + "all_proxy", + "HTTPS_PROXY", + "https_proxy", + "HTTP_PROXY", + "http_proxy", + ]; + + ENV_VARS + .iter() + .find_map(|var| std::env::var(var).ok()) + .and_then(|env| env.parse().ok()) +} + +pub fn read_no_proxy_from_env() -> Option { + const ENV_VARS: &[&str] = &["NO_PROXY", "no_proxy"]; + + ENV_VARS.iter().find_map(|var| std::env::var(var).ok()) +} + +pub struct BlockedHttpClient; + +impl BlockedHttpClient { + pub fn new() -> Self { + BlockedHttpClient + } +} + +impl HttpClient for BlockedHttpClient { + fn send( + &self, + _req: Request, + ) -> BoxFuture<'static, anyhow::Result>> { + Box::pin(async { + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "BlockedHttpClient disallowed request", + ) + .into()) + }) + } + + fn user_agent(&self) -> Option<&HeaderValue> { + None + } + + fn proxy(&self) -> Option<&Url> { + None + } + + fn type_name(&self) -> &'static str { + type_name::() + } + + #[cfg(feature = "test-support")] + fn as_fake(&self) -> &FakeHttpClient { + panic!("called as_fake on {}", type_name::()) + } +} + +#[cfg(feature = "test-support")] +type FakeHttpHandler = Arc< + dyn Fn(Request) -> BoxFuture<'static, anyhow::Result>> + + Send + + Sync + + 'static, +>; + +#[cfg(feature = "test-support")] +pub struct FakeHttpClient { + handler: Mutex>, + user_agent: HeaderValue, +} + +#[cfg(feature = "test-support")] +impl FakeHttpClient { + pub fn create(handler: F) -> Arc + where + Fut: futures::Future>> + Send + 'static, + F: Fn(Request) -> Fut + Send + Sync + 'static, + { + Arc::new(HttpClientWithUrl { + base_url: Mutex::new("http://test.example".into()), + client: HttpClientWithProxy { + client: Arc::new(Self { + handler: Mutex::new(Some(Arc::new(move |req| Box::pin(handler(req))))), + user_agent: HeaderValue::from_static(type_name::()), + }), + proxy: None, + }, + }) + } + + pub fn with_404_response() -> Arc { + Self::create(|_| async move { + Ok(Response::builder() + .status(404) + .body(Default::default()) + .unwrap()) + }) + } + + pub fn with_200_response() -> Arc { + Self::create(|_| async move { + Ok(Response::builder() + .status(200) + .body(Default::default()) + .unwrap()) + }) + } + + pub fn replace_handler(&self, new_handler: F) + where + Fut: futures::Future>> + Send + 'static, + F: Fn(FakeHttpHandler, Request) -> Fut + Send + Sync + 'static, + { + let mut handler = self.handler.lock(); + let old_handler = handler.take().unwrap(); + *handler = Some(Arc::new(move |req| { + Box::pin(new_handler(old_handler.clone(), req)) + })); + } +} + +#[cfg(feature = "test-support")] +impl fmt::Debug for FakeHttpClient { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FakeHttpClient").finish() + } +} + +#[cfg(feature = "test-support")] +impl HttpClient for FakeHttpClient { + fn send( + &self, + req: Request, + ) -> BoxFuture<'static, anyhow::Result>> { + ((self.handler.lock().as_ref().unwrap())(req)) as _ + } + + fn user_agent(&self) -> Option<&HeaderValue> { + Some(&self.user_agent) + } + + fn proxy(&self) -> Option<&Url> { + None + } + + fn type_name(&self) -> &'static str { + type_name::() + } + + fn as_fake(&self) -> &FakeHttpClient { + self + } +} diff --git a/third_party/gpui_support/gpui_sum_tree/Cargo.toml b/third_party/gpui_support/gpui_sum_tree/Cargo.toml new file mode 100644 index 0000000..2e26709 --- /dev/null +++ b/third_party/gpui_support/gpui_sum_tree/Cargo.toml @@ -0,0 +1,66 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# Local copy keeps the registry manifest shape and narrows log features so +# GPUI and Servo can share the same workspace dependency graph. + +[package] +edition = "2024" +name = "gpui_sum_tree" +version = "0.2.2" +build = false +publish = true +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A sum tree data structure, a concurrency-friendly B-tree" +readme = false +license = "Apache-2.0" +resolver = "2" + +[lib] +name = "gpui_sum_tree" +path = "src/sum_tree.rs" +doctest = false + +[dependencies.arrayvec] +version = "0.7.1" + +[dependencies.log] +version = "0.4.16" + +[dependencies.rayon] +version = "1.8" + +[dev-dependencies.ctor] +version = "0.4.0" + +[dev-dependencies.rand] +version = "0.9" + +[lints.clippy] +dbg_macro = "deny" +declare_interior_mutable_const = "deny" +disallowed_methods = "deny" +large_enum_variant = "allow" +let_underscore_future = "allow" +nonminimal_bool = "allow" +redundant_clone = "deny" +single_range_in_vec_init = "allow" +todo = "deny" +too_many_arguments = "allow" +type_complexity = "allow" + +[lints.clippy.style] +level = "allow" +priority = -1 + +[lints.rust.unexpected_cfgs] +level = "allow" +priority = 0 diff --git a/third_party/gpui_support/gpui_sum_tree/LICENSE-APACHE b/third_party/gpui_support/gpui_sum_tree/LICENSE-APACHE new file mode 100644 index 0000000..461a0fe --- /dev/null +++ b/third_party/gpui_support/gpui_sum_tree/LICENSE-APACHE @@ -0,0 +1,222 @@ +Copyright 2022 - 2025 Zed Industries, Inc. + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + + 1. Definitions. + + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + END OF TERMS AND CONDITIONS diff --git a/third_party/gpui_support/gpui_sum_tree/src/cursor.rs b/third_party/gpui_support/gpui_sum_tree/src/cursor.rs new file mode 100644 index 0000000..7418224 --- /dev/null +++ b/third_party/gpui_support/gpui_sum_tree/src/cursor.rs @@ -0,0 +1,819 @@ +use super::*; +use arrayvec::ArrayVec; +use std::{cmp::Ordering, mem, sync::Arc}; + +#[derive(Clone)] +struct StackEntry<'a, T: Item, D> { + tree: &'a SumTree, + index: u32, + position: D, +} + +impl<'a, T: Item, D> StackEntry<'a, T, D> { + #[inline] + fn index(&self) -> usize { + self.index as usize + } +} + +impl fmt::Debug for StackEntry<'_, T, D> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StackEntry") + .field("index", &self.index) + .field("position", &self.position) + .finish() + } +} + +#[derive(Clone)] +pub struct Cursor<'a, 'b, T: Item, D> { + tree: &'a SumTree, + stack: ArrayVec, 16>, + position: D, + did_seek: bool, + at_end: bool, + cx: ::Context<'b>, +} + +impl fmt::Debug for Cursor<'_, '_, T, D> +where + T::Summary: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Cursor") + .field("tree", &self.tree) + .field("stack", &self.stack) + .field("position", &self.position) + .field("did_seek", &self.did_seek) + .field("at_end", &self.at_end) + .finish() + } +} + +pub struct Iter<'a, T: Item> { + tree: &'a SumTree, + stack: ArrayVec, 16>, +} + +impl<'a, 'b, T, D> Cursor<'a, 'b, T, D> +where + T: Item, + D: Dimension<'a, T::Summary>, +{ + pub fn new(tree: &'a SumTree, cx: ::Context<'b>) -> Self { + Self { + tree, + stack: ArrayVec::new(), + position: D::zero(cx), + did_seek: false, + at_end: tree.is_empty(), + cx, + } + } + + fn reset(&mut self) { + self.did_seek = false; + self.at_end = self.tree.is_empty(); + self.stack.truncate(0); + self.position = D::zero(self.cx); + } + + pub fn start(&self) -> &D { + &self.position + } + + #[track_caller] + pub fn end(&self) -> D { + if let Some(item_summary) = self.item_summary() { + let mut end = self.start().clone(); + end.add_summary(item_summary, self.cx); + end + } else { + self.start().clone() + } + } + + /// Item is None, when the list is empty, or this cursor is at the end of the list. + #[track_caller] + pub fn item(&self) -> Option<&'a T> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + match *entry.tree.0 { + Node::Leaf { ref items, .. } => { + if entry.index() == items.len() { + None + } else { + Some(&items[entry.index()]) + } + } + _ => unreachable!(), + } + } else { + None + } + } + + #[track_caller] + pub fn item_summary(&self) -> Option<&'a T::Summary> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + match *entry.tree.0 { + Node::Leaf { + ref item_summaries, .. + } => { + if entry.index() == item_summaries.len() { + None + } else { + Some(&item_summaries[entry.index()]) + } + } + _ => unreachable!(), + } + } else { + None + } + } + + #[track_caller] + pub fn next_item(&self) -> Option<&'a T> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + if entry.index() == entry.tree.0.items().len() - 1 { + if let Some(next_leaf) = self.next_leaf() { + Some(next_leaf.0.items().first().unwrap()) + } else { + None + } + } else { + match *entry.tree.0 { + Node::Leaf { ref items, .. } => Some(&items[entry.index() + 1]), + _ => unreachable!(), + } + } + } else if self.at_end { + None + } else { + self.tree.first() + } + } + + #[track_caller] + fn next_leaf(&self) -> Option<&'a SumTree> { + for entry in self.stack.iter().rev().skip(1) { + if entry.index() < entry.tree.0.child_trees().len() - 1 { + match *entry.tree.0 { + Node::Internal { + ref child_trees, .. + } => return Some(child_trees[entry.index() + 1].leftmost_leaf()), + Node::Leaf { .. } => unreachable!(), + }; + } + } + None + } + + #[track_caller] + pub fn prev_item(&self) -> Option<&'a T> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + if entry.index() == 0 { + if let Some(prev_leaf) = self.prev_leaf() { + Some(prev_leaf.0.items().last().unwrap()) + } else { + None + } + } else { + match *entry.tree.0 { + Node::Leaf { ref items, .. } => Some(&items[entry.index() - 1]), + _ => unreachable!(), + } + } + } else if self.at_end { + self.tree.last() + } else { + None + } + } + + #[track_caller] + fn prev_leaf(&self) -> Option<&'a SumTree> { + for entry in self.stack.iter().rev().skip(1) { + if entry.index() != 0 { + match *entry.tree.0 { + Node::Internal { + ref child_trees, .. + } => return Some(child_trees[entry.index() - 1].rightmost_leaf()), + Node::Leaf { .. } => unreachable!(), + }; + } + } + None + } + + #[track_caller] + pub fn prev(&mut self) { + self.search_backward(|_| true) + } + + #[track_caller] + pub fn search_backward(&mut self, mut filter_node: F) + where + F: FnMut(&T::Summary) -> bool, + { + if !self.did_seek { + self.did_seek = true; + self.at_end = true; + } + + if self.at_end { + self.position = D::zero(self.cx); + self.at_end = self.tree.is_empty(); + if !self.tree.is_empty() { + self.stack.push(StackEntry { + tree: self.tree, + index: self.tree.0.child_summaries().len() as u32, + position: D::from_summary(self.tree.summary(), self.cx), + }); + } + } + + let mut descending = false; + while !self.stack.is_empty() { + if let Some(StackEntry { position, .. }) = self.stack.iter().rev().nth(1) { + self.position = position.clone(); + } else { + self.position = D::zero(self.cx); + } + + let entry = self.stack.last_mut().unwrap(); + if !descending { + if entry.index() == 0 { + self.stack.pop(); + continue; + } else { + entry.index -= 1; + } + } + + for summary in &entry.tree.0.child_summaries()[..entry.index()] { + self.position.add_summary(summary, self.cx); + } + entry.position = self.position.clone(); + + descending = filter_node(&entry.tree.0.child_summaries()[entry.index()]); + match entry.tree.0.as_ref() { + Node::Internal { child_trees, .. } => { + if descending { + let tree = &child_trees[entry.index()]; + self.stack.push(StackEntry { + position: D::zero(self.cx), + tree, + index: tree.0.child_summaries().len() as u32 - 1, + }) + } + } + Node::Leaf { .. } => { + if descending { + break; + } + } + } + } + } + + #[track_caller] + pub fn next(&mut self) { + self.search_forward(|_| true) + } + + #[track_caller] + pub fn search_forward(&mut self, mut filter_node: F) + where + F: FnMut(&T::Summary) -> bool, + { + let mut descend = false; + + if self.stack.is_empty() { + if !self.at_end { + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + position: D::zero(self.cx), + }); + descend = true; + } + self.did_seek = true; + } + + while !self.stack.is_empty() { + let new_subtree = { + let entry = self.stack.last_mut().unwrap(); + match entry.tree.0.as_ref() { + Node::Internal { + child_trees, + child_summaries, + .. + } => { + if !descend { + entry.index += 1; + entry.position = self.position.clone(); + } + + while entry.index() < child_summaries.len() { + let next_summary = &child_summaries[entry.index()]; + if filter_node(next_summary) { + break; + } else { + entry.index += 1; + entry.position.add_summary(next_summary, self.cx); + self.position.add_summary(next_summary, self.cx); + } + } + + child_trees.get(entry.index()) + } + Node::Leaf { item_summaries, .. } => { + if !descend { + let item_summary = &item_summaries[entry.index()]; + entry.index += 1; + entry.position.add_summary(item_summary, self.cx); + self.position.add_summary(item_summary, self.cx); + } + + loop { + if let Some(next_item_summary) = item_summaries.get(entry.index()) { + if filter_node(next_item_summary) { + return; + } else { + entry.index += 1; + entry.position.add_summary(next_item_summary, self.cx); + self.position.add_summary(next_item_summary, self.cx); + } + } else { + break None; + } + } + } + } + }; + + if let Some(subtree) = new_subtree { + descend = true; + self.stack.push(StackEntry { + tree: subtree, + index: 0, + position: self.position.clone(), + }); + } else { + descend = false; + self.stack.pop(); + } + } + + self.at_end = self.stack.is_empty(); + debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf()); + } + + #[track_caller] + fn assert_did_seek(&self) { + assert!( + self.did_seek, + "Must call `seek`, `next` or `prev` before calling this method" + ); + } +} + +impl<'a, 'b, T, D> Cursor<'a, 'b, T, D> +where + T: Item, + D: Dimension<'a, T::Summary>, +{ + /// Returns whether we found the item you were seeking for. + #[track_caller] + pub fn seek(&mut self, pos: &Target, bias: Bias) -> bool + where + Target: SeekTarget<'a, T::Summary, D>, + { + self.reset(); + self.seek_internal(pos, bias, &mut ()) + } + + /// Returns whether we found the item you were seeking for. + #[track_caller] + pub fn seek_forward(&mut self, pos: &Target, bias: Bias) -> bool + where + Target: SeekTarget<'a, T::Summary, D>, + { + self.seek_internal(pos, bias, &mut ()) + } + + /// Advances the cursor and returns traversed items as a tree. + #[track_caller] + pub fn slice(&mut self, end: &Target, bias: Bias) -> SumTree + where + Target: SeekTarget<'a, T::Summary, D>, + { + let mut slice = SliceSeekAggregate { + tree: SumTree::new(self.cx), + leaf_items: ArrayVec::new(), + leaf_item_summaries: ArrayVec::new(), + leaf_summary: ::zero(self.cx), + }; + self.seek_internal(end, bias, &mut slice); + slice.tree + } + + #[track_caller] + pub fn suffix(&mut self) -> SumTree { + self.slice(&End::new(), Bias::Right) + } + + #[track_caller] + pub fn summary(&mut self, end: &Target, bias: Bias) -> Output + where + Target: SeekTarget<'a, T::Summary, D>, + Output: Dimension<'a, T::Summary>, + { + let mut summary = SummarySeekAggregate(Output::zero(self.cx)); + self.seek_internal(end, bias, &mut summary); + summary.0 + } + + /// Returns whether we found the item you were seeking for. + #[track_caller] + fn seek_internal( + &mut self, + target: &dyn SeekTarget<'a, T::Summary, D>, + bias: Bias, + aggregate: &mut dyn SeekAggregate<'a, T>, + ) -> bool { + assert!( + target.cmp(&self.position, self.cx) >= Ordering::Equal, + "cannot seek backward", + ); + + if !self.did_seek { + self.did_seek = true; + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + position: D::zero(self.cx), + }); + } + + let mut ascending = false; + 'outer: while let Some(entry) = self.stack.last_mut() { + match *entry.tree.0 { + Node::Internal { + ref child_summaries, + ref child_trees, + .. + } => { + if ascending { + entry.index += 1; + entry.position = self.position.clone(); + } + + for (child_tree, child_summary) in child_trees[entry.index()..] + .iter() + .zip(&child_summaries[entry.index()..]) + { + let mut child_end = self.position.clone(); + child_end.add_summary(child_summary, self.cx); + + let comparison = target.cmp(&child_end, self.cx); + if comparison == Ordering::Greater + || (comparison == Ordering::Equal && bias == Bias::Right) + { + self.position = child_end; + aggregate.push_tree(child_tree, child_summary, self.cx); + entry.index += 1; + entry.position = self.position.clone(); + } else { + self.stack.push(StackEntry { + tree: child_tree, + index: 0, + position: self.position.clone(), + }); + ascending = false; + continue 'outer; + } + } + } + Node::Leaf { + ref items, + ref item_summaries, + .. + } => { + aggregate.begin_leaf(); + + for (item, item_summary) in items[entry.index()..] + .iter() + .zip(&item_summaries[entry.index()..]) + { + let mut child_end = self.position.clone(); + child_end.add_summary(item_summary, self.cx); + + let comparison = target.cmp(&child_end, self.cx); + if comparison == Ordering::Greater + || (comparison == Ordering::Equal && bias == Bias::Right) + { + self.position = child_end; + aggregate.push_item(item, item_summary, self.cx); + entry.index += 1; + } else { + aggregate.end_leaf(self.cx); + break 'outer; + } + } + + aggregate.end_leaf(self.cx); + } + } + + self.stack.pop(); + ascending = true; + } + + self.at_end = self.stack.is_empty(); + debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf()); + + let mut end = self.position.clone(); + if bias == Bias::Left + && let Some(summary) = self.item_summary() + { + end.add_summary(summary, self.cx); + } + + target.cmp(&end, self.cx) == Ordering::Equal + } +} + +impl<'a, T: Item> Iter<'a, T> { + pub(crate) fn new(tree: &'a SumTree) -> Self { + Self { + tree, + stack: Default::default(), + } + } +} + +impl<'a, T: Item> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + let mut descend = false; + + if self.stack.is_empty() { + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + position: (), + }); + descend = true; + } + + while !self.stack.is_empty() { + let new_subtree = { + let entry = self.stack.last_mut().unwrap(); + match entry.tree.0.as_ref() { + Node::Internal { child_trees, .. } => { + if !descend { + entry.index += 1; + } + child_trees.get(entry.index()) + } + Node::Leaf { items, .. } => { + if !descend { + entry.index += 1; + } + + if let Some(next_item) = items.get(entry.index()) { + return Some(next_item); + } else { + None + } + } + } + }; + + if let Some(subtree) = new_subtree { + descend = true; + self.stack.push(StackEntry { + tree: subtree, + index: 0, + position: (), + }); + } else { + descend = false; + self.stack.pop(); + } + } + + None + } +} + +impl<'a, 'b, T: Item, D> Iterator for Cursor<'a, 'b, T, D> +where + D: Dimension<'a, T::Summary>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if !self.did_seek { + self.next(); + } + + if let Some(item) = self.item() { + self.next(); + Some(item) + } else { + None + } + } +} + +pub struct FilterCursor<'a, 'b, F, T: Item, D> { + cursor: Cursor<'a, 'b, T, D>, + filter_node: F, +} + +impl<'a, 'b, F, T: Item, D> FilterCursor<'a, 'b, F, T, D> +where + F: FnMut(&T::Summary) -> bool, + T: Item, + D: Dimension<'a, T::Summary>, +{ + pub fn new( + tree: &'a SumTree, + cx: ::Context<'b>, + filter_node: F, + ) -> Self { + let cursor = tree.cursor::(cx); + Self { + cursor, + filter_node, + } + } + + pub fn start(&self) -> &D { + self.cursor.start() + } + + pub fn end(&self) -> D { + self.cursor.end() + } + + pub fn item(&self) -> Option<&'a T> { + self.cursor.item() + } + + pub fn item_summary(&self) -> Option<&'a T::Summary> { + self.cursor.item_summary() + } + + pub fn next(&mut self) { + self.cursor.search_forward(&mut self.filter_node); + } + + pub fn prev(&mut self) { + self.cursor.search_backward(&mut self.filter_node); + } +} + +impl<'a, 'b, F, T: Item, U> Iterator for FilterCursor<'a, 'b, F, T, U> +where + F: FnMut(&T::Summary) -> bool, + U: Dimension<'a, T::Summary>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if !self.cursor.did_seek { + self.next(); + } + + if let Some(item) = self.item() { + self.cursor.search_forward(&mut self.filter_node); + Some(item) + } else { + None + } + } +} + +trait SeekAggregate<'a, T: Item> { + fn begin_leaf(&mut self); + fn end_leaf(&mut self, cx: ::Context<'_>); + fn push_item( + &mut self, + item: &'a T, + summary: &'a T::Summary, + cx: ::Context<'_>, + ); + fn push_tree( + &mut self, + tree: &'a SumTree, + summary: &'a T::Summary, + cx: ::Context<'_>, + ); +} + +struct SliceSeekAggregate { + tree: SumTree, + leaf_items: ArrayVec, + leaf_item_summaries: ArrayVec, + leaf_summary: T::Summary, +} + +struct SummarySeekAggregate(D); + +impl SeekAggregate<'_, T> for () { + fn begin_leaf(&mut self) {} + fn end_leaf(&mut self, _: ::Context<'_>) {} + fn push_item(&mut self, _: &T, _: &T::Summary, _: ::Context<'_>) {} + fn push_tree( + &mut self, + _: &SumTree, + _: &T::Summary, + _: ::Context<'_>, + ) { + } +} + +impl SeekAggregate<'_, T> for SliceSeekAggregate { + fn begin_leaf(&mut self) {} + fn end_leaf(&mut self, cx: ::Context<'_>) { + self.tree.append( + SumTree(Arc::new(Node::Leaf { + summary: mem::replace(&mut self.leaf_summary, ::zero(cx)), + items: mem::take(&mut self.leaf_items), + item_summaries: mem::take(&mut self.leaf_item_summaries), + })), + cx, + ); + } + fn push_item( + &mut self, + item: &T, + summary: &T::Summary, + cx: ::Context<'_>, + ) { + self.leaf_items.push(item.clone()); + self.leaf_item_summaries.push(summary.clone()); + Summary::add_summary(&mut self.leaf_summary, summary, cx); + } + fn push_tree( + &mut self, + tree: &SumTree, + _: &T::Summary, + cx: ::Context<'_>, + ) { + self.tree.append(tree.clone(), cx); + } +} + +impl<'a, T: Item, D> SeekAggregate<'a, T> for SummarySeekAggregate +where + D: Dimension<'a, T::Summary>, +{ + fn begin_leaf(&mut self) {} + fn end_leaf(&mut self, _: ::Context<'_>) {} + fn push_item( + &mut self, + _: &T, + summary: &'a T::Summary, + cx: ::Context<'_>, + ) { + self.0.add_summary(summary, cx); + } + fn push_tree( + &mut self, + _: &SumTree, + summary: &'a T::Summary, + cx: ::Context<'_>, + ) { + self.0.add_summary(summary, cx); + } +} + +struct End(PhantomData); + +impl End { + fn new() -> Self { + Self(PhantomData) + } +} + +impl<'a, S: Summary, D: Dimension<'a, S>> SeekTarget<'a, S, D> for End { + fn cmp(&self, _: &D, _: S::Context<'_>) -> Ordering { + Ordering::Greater + } +} + +impl fmt::Debug for End { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("End").finish() + } +} diff --git a/third_party/gpui_support/gpui_sum_tree/src/sum_tree.rs b/third_party/gpui_support/gpui_sum_tree/src/sum_tree.rs new file mode 100644 index 0000000..ab0e9d0 --- /dev/null +++ b/third_party/gpui_support/gpui_sum_tree/src/sum_tree.rs @@ -0,0 +1,1558 @@ +mod cursor; +mod tree_map; + +use arrayvec::ArrayVec; +pub use cursor::{Cursor, FilterCursor, Iter}; +use rayon::prelude::*; +use std::marker::PhantomData; +use std::mem; +use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc}; +pub use tree_map::{MapSeekTarget, TreeMap, TreeSet}; + +#[cfg(test)] +pub const TREE_BASE: usize = 2; +#[cfg(not(test))] +pub const TREE_BASE: usize = 6; + +/// An item that can be stored in a [`SumTree`] +/// +/// Must be summarized by a type that implements [`Summary`] +pub trait Item: Clone { + type Summary: Summary; + + fn summary(&self, cx: ::Context<'_>) -> Self::Summary; +} + +/// An [`Item`] whose summary has a specific key that can be used to identify it +pub trait KeyedItem: Item { + type Key: for<'a> Dimension<'a, Self::Summary> + Ord; + + fn key(&self) -> Self::Key; +} + +/// A type that describes the Sum of all [`Item`]s in a subtree of the [`SumTree`] +/// +/// Each Summary type can have multiple [`Dimension`]s that it measures, +/// which can be used to navigate the tree +pub trait Summary: Clone { + type Context<'a>: Copy; + fn zero<'a>(cx: Self::Context<'a>) -> Self; + fn add_summary<'a>(&mut self, summary: &Self, cx: Self::Context<'a>); +} + +pub trait ContextLessSummary: Clone { + fn zero() -> Self; + fn add_summary(&mut self, summary: &Self); +} + +impl Summary for T { + type Context<'a> = (); + + fn zero<'a>((): ()) -> Self { + T::zero() + } + + fn add_summary<'a>(&mut self, summary: &Self, (): ()) { + T::add_summary(self, summary) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct NoSummary; + +/// Catch-all implementation for when you need something that implements [`Summary`] without a specific type. +/// We implement it on a `NoSummary` instead of re-using `()`, as that avoids blanket impl collisions with `impl Dimension for T` +/// (as we also need unit type to be a fill-in dimension) +impl ContextLessSummary for NoSummary { + fn zero() -> Self { + NoSummary + } + + fn add_summary(&mut self, _: &Self) {} +} + +/// Each [`Summary`] type can have more than one [`Dimension`] type that it measures. +/// +/// You can use dimensions to seek to a specific location in the [`SumTree`] +/// +/// # Example: +/// Zed's rope has a `TextSummary` type that summarizes lines, characters, and bytes. +/// Each of these are different dimensions we may want to seek to +pub trait Dimension<'a, S: Summary>: Clone { + fn zero(cx: S::Context<'_>) -> Self; + + fn add_summary(&mut self, summary: &'a S, cx: S::Context<'_>); + #[must_use] + fn with_added_summary(mut self, summary: &'a S, cx: S::Context<'_>) -> Self { + self.add_summary(summary, cx); + self + } + + fn from_summary(summary: &'a S, cx: S::Context<'_>) -> Self { + let mut dimension = Self::zero(cx); + dimension.add_summary(summary, cx); + dimension + } +} + +impl<'a, T: Summary> Dimension<'a, T> for T { + fn zero(cx: T::Context<'_>) -> Self { + Summary::zero(cx) + } + + fn add_summary(&mut self, summary: &'a T, cx: T::Context<'_>) { + Summary::add_summary(self, summary, cx); + } +} + +pub trait SeekTarget<'a, S: Summary, D: Dimension<'a, S>> { + fn cmp(&self, cursor_location: &D, cx: S::Context<'_>) -> Ordering; +} + +impl<'a, S: Summary, D: Dimension<'a, S> + Ord> SeekTarget<'a, S, D> for D { + fn cmp(&self, cursor_location: &Self, _: S::Context<'_>) -> Ordering { + Ord::cmp(self, cursor_location) + } +} + +impl<'a, T: Summary> Dimension<'a, T> for () { + fn zero(_: T::Context<'_>) -> Self {} + + fn add_summary(&mut self, _: &'a T, _: T::Context<'_>) {} +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Dimensions(pub D1, pub D2, pub D3); + +impl<'a, T: Summary, D1: Dimension<'a, T>, D2: Dimension<'a, T>, D3: Dimension<'a, T>> + Dimension<'a, T> for Dimensions +{ + fn zero(cx: T::Context<'_>) -> Self { + Dimensions(D1::zero(cx), D2::zero(cx), D3::zero(cx)) + } + + fn add_summary(&mut self, summary: &'a T, cx: T::Context<'_>) { + self.0.add_summary(summary, cx); + self.1.add_summary(summary, cx); + self.2.add_summary(summary, cx); + } +} + +impl<'a, S, D1, D2, D3> SeekTarget<'a, S, Dimensions> for D1 +where + S: Summary, + D1: SeekTarget<'a, S, D1> + Dimension<'a, S>, + D2: Dimension<'a, S>, + D3: Dimension<'a, S>, +{ + fn cmp(&self, cursor_location: &Dimensions, cx: S::Context<'_>) -> Ordering { + self.cmp(&cursor_location.0, cx) + } +} + +/// Bias is used to settle ambiguities when determining positions in an ordered sequence. +/// +/// The primary use case is for text, where Bias influences +/// which character an offset or anchor is associated with. +/// +/// # Examples +/// Given the buffer `AˇBCD`: +/// - The offset of the cursor is 1 +/// - [Bias::Left] would attach the cursor to the character `A` +/// - [Bias::Right] would attach the cursor to the character `B` +/// +/// Given the buffer `A«BCˇ»D`: +/// - The offset of the cursor is 3, and the selection is from 1 to 3 +/// - The left anchor of the selection has [Bias::Right], attaching it to the character `B` +/// - The right anchor of the selection has [Bias::Left], attaching it to the character `C` +/// +/// Given the buffer `{ˇ<...>`, where `<...>` is a folded region: +/// - The display offset of the cursor is 1, but the offset in the buffer is determined by the bias +/// - [Bias::Left] would attach the cursor to the character `{`, with a buffer offset of 1 +/// - [Bias::Right] would attach the cursor to the first character of the folded region, +/// and the buffer offset would be the offset of the first character of the folded region +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Default)] +pub enum Bias { + /// Attach to the character on the left + #[default] + Left, + /// Attach to the character on the right + Right, +} + +impl Bias { + pub fn invert(self) -> Self { + match self { + Self::Left => Self::Right, + Self::Right => Self::Left, + } + } +} + +/// A B+ tree in which each leaf node contains `Item`s of type `T` and a `Summary`s for each `Item`. +/// Each internal node contains a `Summary` of the items in its subtree. +/// +/// The maximum number of items per node is `TREE_BASE * 2`. +/// +/// Any [`Dimension`] supported by the [`Summary`] type can be used to seek to a specific location in the tree. +#[derive(Clone)] +pub struct SumTree(Arc>); + +impl fmt::Debug for SumTree +where + T: fmt::Debug + Item, + T::Summary: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SumTree").field(&self.0).finish() + } +} + +impl SumTree { + pub fn new(cx: ::Context<'_>) -> Self { + SumTree(Arc::new(Node::Leaf { + summary: ::zero(cx), + items: ArrayVec::new(), + item_summaries: ArrayVec::new(), + })) + } + + /// Useful in cases where the item type has a non-trivial context type, but the zero value of the summary type doesn't depend on that context. + pub fn from_summary(summary: T::Summary) -> Self { + SumTree(Arc::new(Node::Leaf { + summary, + items: ArrayVec::new(), + item_summaries: ArrayVec::new(), + })) + } + + pub fn from_item(item: T, cx: ::Context<'_>) -> Self { + let mut tree = Self::new(cx); + tree.push(item, cx); + tree + } + + pub fn from_iter>( + iter: I, + cx: ::Context<'_>, + ) -> Self { + let mut nodes = Vec::new(); + + let mut iter = iter.into_iter().fuse().peekable(); + while iter.peek().is_some() { + let items: ArrayVec = iter.by_ref().take(2 * TREE_BASE).collect(); + let item_summaries: ArrayVec = + items.iter().map(|item| item.summary(cx)).collect(); + + let mut summary = item_summaries[0].clone(); + for item_summary in &item_summaries[1..] { + ::add_summary(&mut summary, item_summary, cx); + } + + nodes.push(Node::Leaf { + summary, + items, + item_summaries, + }); + } + + let mut parent_nodes = Vec::new(); + let mut height = 0; + while nodes.len() > 1 { + height += 1; + let mut current_parent_node = None; + for child_node in nodes.drain(..) { + let parent_node = current_parent_node.get_or_insert_with(|| Node::Internal { + summary: ::zero(cx), + height, + child_summaries: ArrayVec::new(), + child_trees: ArrayVec::new(), + }); + let Node::Internal { + summary, + child_summaries, + child_trees, + .. + } = parent_node + else { + unreachable!() + }; + let child_summary = child_node.summary(); + ::add_summary(summary, child_summary, cx); + child_summaries.push(child_summary.clone()); + child_trees.push(Self(Arc::new(child_node))); + + if child_trees.len() == 2 * TREE_BASE { + parent_nodes.extend(current_parent_node.take()); + } + } + parent_nodes.extend(current_parent_node.take()); + mem::swap(&mut nodes, &mut parent_nodes); + } + + if nodes.is_empty() { + Self::new(cx) + } else { + debug_assert_eq!(nodes.len(), 1); + Self(Arc::new(nodes.pop().unwrap())) + } + } + + pub fn from_par_iter(iter: I, cx: ::Context<'_>) -> Self + where + I: IntoParallelIterator, + Iter: IndexedParallelIterator, + T: Send + Sync, + T::Summary: Send + Sync, + for<'a> ::Context<'a>: Sync, + { + let mut nodes = iter + .into_par_iter() + .chunks(2 * TREE_BASE) + .map(|items| { + let items: ArrayVec = items.into_iter().collect(); + let item_summaries: ArrayVec = + items.iter().map(|item| item.summary(cx)).collect(); + let mut summary = item_summaries[0].clone(); + for item_summary in &item_summaries[1..] { + ::add_summary(&mut summary, item_summary, cx); + } + SumTree(Arc::new(Node::Leaf { + summary, + items, + item_summaries, + })) + }) + .collect::>(); + + let mut height = 0; + while nodes.len() > 1 { + height += 1; + nodes = nodes + .into_par_iter() + .chunks(2 * TREE_BASE) + .map(|child_nodes| { + let child_trees: ArrayVec, { 2 * TREE_BASE }> = + child_nodes.into_iter().collect(); + let child_summaries: ArrayVec = child_trees + .iter() + .map(|child_tree| child_tree.summary().clone()) + .collect(); + let mut summary = child_summaries[0].clone(); + for child_summary in &child_summaries[1..] { + ::add_summary(&mut summary, child_summary, cx); + } + SumTree(Arc::new(Node::Internal { + height, + summary, + child_summaries, + child_trees, + })) + }) + .collect::>(); + } + + if nodes.is_empty() { + Self::new(cx) + } else { + debug_assert_eq!(nodes.len(), 1); + nodes.pop().unwrap() + } + } + + #[allow(unused)] + pub fn items<'a>(&'a self, cx: ::Context<'a>) -> Vec { + let mut items = Vec::new(); + let mut cursor = self.cursor::<()>(cx); + cursor.next(); + while let Some(item) = cursor.item() { + items.push(item.clone()); + cursor.next(); + } + items + } + + pub fn iter(&self) -> Iter<'_, T> { + Iter::new(self) + } + + /// A more efficient version of `Cursor::new()` + `Cursor::seek()` + `Cursor::item()`. + /// + /// Only returns the item that exactly has the target match. + pub fn find_exact<'a, 'slf, D, Target>( + &'slf self, + cx: ::Context<'a>, + target: &Target, + bias: Bias, + ) -> (D, D, Option<&'slf T>) + where + D: Dimension<'slf, T::Summary>, + Target: SeekTarget<'slf, T::Summary, D>, + { + let tree_end = D::zero(cx).with_added_summary(self.summary(), cx); + let comparison = target.cmp(&tree_end, cx); + if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == Bias::Right) + { + return (tree_end.clone(), tree_end, None); + } + + let mut pos = D::zero(cx); + return match Self::find_recurse::<_, _, true>(cx, target, bias, &mut pos, self) { + Some((item, end)) => (pos, end, Some(item)), + None => (pos.clone(), pos, None), + }; + } + + /// A more efficient version of `Cursor::new()` + `Cursor::seek()` + `Cursor::item()` + pub fn find<'a, 'slf, D, Target>( + &'slf self, + cx: ::Context<'a>, + target: &Target, + bias: Bias, + ) -> (D, D, Option<&'slf T>) + where + D: Dimension<'slf, T::Summary>, + Target: SeekTarget<'slf, T::Summary, D>, + { + let tree_end = D::zero(cx).with_added_summary(self.summary(), cx); + let comparison = target.cmp(&tree_end, cx); + if comparison == Ordering::Greater || (comparison == Ordering::Equal && bias == Bias::Right) + { + return (tree_end.clone(), tree_end, None); + } + + let mut pos = D::zero(cx); + return match Self::find_recurse::<_, _, false>(cx, target, bias, &mut pos, self) { + Some((item, end)) => (pos, end, Some(item)), + None => (pos.clone(), pos, None), + }; + } + + fn find_recurse<'tree, 'a, D, Target, const EXACT: bool>( + cx: ::Context<'a>, + target: &Target, + bias: Bias, + position: &mut D, + this: &'tree SumTree, + ) -> Option<(&'tree T, D)> + where + D: Dimension<'tree, T::Summary>, + Target: SeekTarget<'tree, T::Summary, D>, + { + match &*this.0 { + Node::Internal { + child_summaries, + child_trees, + .. + } => { + for (child_tree, child_summary) in child_trees.iter().zip(child_summaries) { + let child_end = position.clone().with_added_summary(child_summary, cx); + + let comparison = target.cmp(&child_end, cx); + let target_in_child = comparison == Ordering::Less + || (comparison == Ordering::Equal && bias == Bias::Left); + if target_in_child { + return Self::find_recurse::( + cx, target, bias, position, child_tree, + ); + } + *position = child_end; + } + } + Node::Leaf { + items, + item_summaries, + .. + } => { + for (item, item_summary) in items.iter().zip(item_summaries) { + let mut child_end = position.clone(); + child_end.add_summary(item_summary, cx); + + let comparison = target.cmp(&child_end, cx); + let entry_found = if EXACT { + comparison == Ordering::Equal + } else { + comparison == Ordering::Less + || (comparison == Ordering::Equal && bias == Bias::Left) + }; + if entry_found { + return Some((item, child_end)); + } + + *position = child_end; + } + } + } + None + } + + pub fn cursor<'a, 'b, D>( + &'a self, + cx: ::Context<'b>, + ) -> Cursor<'a, 'b, T, D> + where + D: Dimension<'a, T::Summary>, + { + Cursor::new(self, cx) + } + + /// Note: If the summary type requires a non `()` context, then the filter cursor + /// that is returned cannot be used with Rust's iterators. + pub fn filter<'a, 'b, F, U>( + &'a self, + cx: ::Context<'b>, + filter_node: F, + ) -> FilterCursor<'a, 'b, F, T, U> + where + F: FnMut(&T::Summary) -> bool, + U: Dimension<'a, T::Summary>, + { + FilterCursor::new(self, cx, filter_node) + } + + #[allow(dead_code)] + pub fn first(&self) -> Option<&T> { + self.leftmost_leaf().0.items().first() + } + + pub fn last(&self) -> Option<&T> { + self.rightmost_leaf().0.items().last() + } + + pub fn update_last( + &mut self, + f: impl FnOnce(&mut T), + cx: ::Context<'_>, + ) { + self.update_last_recursive(f, cx); + } + + fn update_last_recursive( + &mut self, + f: impl FnOnce(&mut T), + cx: ::Context<'_>, + ) -> Option { + match Arc::make_mut(&mut self.0) { + Node::Internal { + summary, + child_summaries, + child_trees, + .. + } => { + let last_summary = child_summaries.last_mut().unwrap(); + let last_child = child_trees.last_mut().unwrap(); + *last_summary = last_child.update_last_recursive(f, cx).unwrap(); + *summary = sum(child_summaries.iter(), cx); + Some(summary.clone()) + } + Node::Leaf { + summary, + items, + item_summaries, + } => { + if let Some((item, item_summary)) = items.last_mut().zip(item_summaries.last_mut()) + { + (f)(item); + *item_summary = item.summary(cx); + *summary = sum(item_summaries.iter(), cx); + Some(summary.clone()) + } else { + None + } + } + } + } + + pub fn extent<'a, D: Dimension<'a, T::Summary>>( + &'a self, + cx: ::Context<'_>, + ) -> D { + let mut extent = D::zero(cx); + match self.0.as_ref() { + Node::Internal { summary, .. } | Node::Leaf { summary, .. } => { + extent.add_summary(summary, cx); + } + } + extent + } + + pub fn summary(&self) -> &T::Summary { + match self.0.as_ref() { + Node::Internal { summary, .. } => summary, + Node::Leaf { summary, .. } => summary, + } + } + + pub fn is_empty(&self) -> bool { + match self.0.as_ref() { + Node::Internal { .. } => false, + Node::Leaf { items, .. } => items.is_empty(), + } + } + + pub fn extend(&mut self, iter: I, cx: ::Context<'_>) + where + I: IntoIterator, + { + self.append(Self::from_iter(iter, cx), cx); + } + + pub fn par_extend(&mut self, iter: I, cx: ::Context<'_>) + where + I: IntoParallelIterator, + Iter: IndexedParallelIterator, + T: Send + Sync, + T::Summary: Send + Sync, + for<'a> ::Context<'a>: Sync, + { + self.append(Self::from_par_iter(iter, cx), cx); + } + + pub fn push(&mut self, item: T, cx: ::Context<'_>) { + let summary = item.summary(cx); + self.append( + SumTree(Arc::new(Node::Leaf { + summary: summary.clone(), + items: ArrayVec::from_iter(Some(item)), + item_summaries: ArrayVec::from_iter(Some(summary)), + })), + cx, + ); + } + + pub fn append(&mut self, other: Self, cx: ::Context<'_>) { + if self.is_empty() { + *self = other; + } else if !other.0.is_leaf() || !other.0.items().is_empty() { + if self.0.height() < other.0.height() { + for tree in other.0.child_trees() { + self.append(tree.clone(), cx); + } + } else if let Some(split_tree) = self.push_tree_recursive(other, cx) { + *self = Self::from_child_trees(self.clone(), split_tree, cx); + } + } + } + + fn push_tree_recursive( + &mut self, + other: SumTree, + cx: ::Context<'_>, + ) -> Option> { + match Arc::make_mut(&mut self.0) { + Node::Internal { + height, + summary, + child_summaries, + child_trees, + .. + } => { + let other_node = other.0.clone(); + ::add_summary(summary, other_node.summary(), cx); + + let height_delta = *height - other_node.height(); + let mut summaries_to_append = ArrayVec::::new(); + let mut trees_to_append = ArrayVec::, { 2 * TREE_BASE }>::new(); + if height_delta == 0 { + summaries_to_append.extend(other_node.child_summaries().iter().cloned()); + trees_to_append.extend(other_node.child_trees().iter().cloned()); + } else if height_delta == 1 && !other_node.is_underflowing() { + summaries_to_append.push(other_node.summary().clone()); + trees_to_append.push(other) + } else { + let tree_to_append = child_trees + .last_mut() + .unwrap() + .push_tree_recursive(other, cx); + *child_summaries.last_mut().unwrap() = + child_trees.last().unwrap().0.summary().clone(); + + if let Some(split_tree) = tree_to_append { + summaries_to_append.push(split_tree.0.summary().clone()); + trees_to_append.push(split_tree); + } + } + + let child_count = child_trees.len() + trees_to_append.len(); + if child_count > 2 * TREE_BASE { + let left_summaries: ArrayVec<_, { 2 * TREE_BASE }>; + let right_summaries: ArrayVec<_, { 2 * TREE_BASE }>; + let left_trees; + let right_trees; + + let midpoint = (child_count + child_count % 2) / 2; + { + let mut all_summaries = child_summaries + .iter() + .chain(summaries_to_append.iter()) + .cloned(); + left_summaries = all_summaries.by_ref().take(midpoint).collect(); + right_summaries = all_summaries.collect(); + let mut all_trees = + child_trees.iter().chain(trees_to_append.iter()).cloned(); + left_trees = all_trees.by_ref().take(midpoint).collect(); + right_trees = all_trees.collect(); + } + *summary = sum(left_summaries.iter(), cx); + *child_summaries = left_summaries; + *child_trees = left_trees; + + Some(SumTree(Arc::new(Node::Internal { + height: *height, + summary: sum(right_summaries.iter(), cx), + child_summaries: right_summaries, + child_trees: right_trees, + }))) + } else { + child_summaries.extend(summaries_to_append); + child_trees.extend(trees_to_append); + None + } + } + Node::Leaf { + summary, + items, + item_summaries, + } => { + let other_node = other.0; + + let child_count = items.len() + other_node.items().len(); + if child_count > 2 * TREE_BASE { + let left_items; + let right_items; + let left_summaries; + let right_summaries: ArrayVec; + + let midpoint = (child_count + child_count % 2) / 2; + { + let mut all_items = items.iter().chain(other_node.items().iter()).cloned(); + left_items = all_items.by_ref().take(midpoint).collect(); + right_items = all_items.collect(); + + let mut all_summaries = item_summaries + .iter() + .chain(other_node.child_summaries()) + .cloned(); + left_summaries = all_summaries.by_ref().take(midpoint).collect(); + right_summaries = all_summaries.collect(); + } + *items = left_items; + *item_summaries = left_summaries; + *summary = sum(item_summaries.iter(), cx); + Some(SumTree(Arc::new(Node::Leaf { + items: right_items, + summary: sum(right_summaries.iter(), cx), + item_summaries: right_summaries, + }))) + } else { + ::add_summary(summary, other_node.summary(), cx); + items.extend(other_node.items().iter().cloned()); + item_summaries.extend(other_node.child_summaries().iter().cloned()); + None + } + } + } + } + + fn from_child_trees( + left: SumTree, + right: SumTree, + cx: ::Context<'_>, + ) -> Self { + let height = left.0.height() + 1; + let mut child_summaries = ArrayVec::new(); + child_summaries.push(left.0.summary().clone()); + child_summaries.push(right.0.summary().clone()); + let mut child_trees = ArrayVec::new(); + child_trees.push(left); + child_trees.push(right); + SumTree(Arc::new(Node::Internal { + height, + summary: sum(child_summaries.iter(), cx), + child_summaries, + child_trees, + })) + } + + fn leftmost_leaf(&self) -> &Self { + match *self.0 { + Node::Leaf { .. } => self, + Node::Internal { + ref child_trees, .. + } => child_trees.first().unwrap().leftmost_leaf(), + } + } + + fn rightmost_leaf(&self) -> &Self { + match *self.0 { + Node::Leaf { .. } => self, + Node::Internal { + ref child_trees, .. + } => child_trees.last().unwrap().rightmost_leaf(), + } + } +} + +impl PartialEq for SumTree { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} + +impl Eq for SumTree {} + +impl SumTree { + pub fn insert_or_replace<'a, 'b>( + &'a mut self, + item: T, + cx: ::Context<'b>, + ) -> Option { + let mut replaced = None; + { + let mut cursor = self.cursor::(cx); + let mut new_tree = cursor.slice(&item.key(), Bias::Left); + if let Some(cursor_item) = cursor.item() + && cursor_item.key() == item.key() + { + replaced = Some(cursor_item.clone()); + cursor.next(); + } + new_tree.push(item, cx); + new_tree.append(cursor.suffix(), cx); + drop(cursor); + *self = new_tree + }; + replaced + } + + pub fn remove(&mut self, key: &T::Key, cx: ::Context<'_>) -> Option { + let mut removed = None; + *self = { + let mut cursor = self.cursor::(cx); + let mut new_tree = cursor.slice(key, Bias::Left); + if let Some(item) = cursor.item() + && item.key() == *key + { + removed = Some(item.clone()); + cursor.next(); + } + new_tree.append(cursor.suffix(), cx); + new_tree + }; + removed + } + + pub fn edit( + &mut self, + mut edits: Vec>, + cx: ::Context<'_>, + ) -> Vec { + if edits.is_empty() { + return Vec::new(); + } + + let mut removed = Vec::new(); + edits.sort_unstable_by_key(|item| item.key()); + + *self = { + let mut cursor = self.cursor::(cx); + let mut new_tree = SumTree::new(cx); + let mut buffered_items = Vec::new(); + + cursor.seek(&T::Key::zero(cx), Bias::Left); + for edit in edits { + let new_key = edit.key(); + let mut old_item = cursor.item(); + + if old_item + .as_ref() + .is_some_and(|old_item| old_item.key() < new_key) + { + new_tree.extend(buffered_items.drain(..), cx); + let slice = cursor.slice(&new_key, Bias::Left); + new_tree.append(slice, cx); + old_item = cursor.item(); + } + + if let Some(old_item) = old_item + && old_item.key() == new_key + { + removed.push(old_item.clone()); + cursor.next(); + } + + match edit { + Edit::Insert(item) => { + buffered_items.push(item); + } + Edit::Remove(_) => {} + } + } + + new_tree.extend(buffered_items, cx); + new_tree.append(cursor.suffix(), cx); + new_tree + }; + + removed + } + + pub fn get<'a>( + &'a self, + key: &T::Key, + cx: ::Context<'a>, + ) -> Option<&'a T> { + if let (_, _, Some(item)) = self.find_exact::(cx, key, Bias::Left) { + Some(item) + } else { + None + } + } +} + +impl Default for SumTree +where + T: Item

, + S: for<'a> Summary = ()>, +{ + fn default() -> Self { + Self::new(()) + } +} + +#[derive(Clone)] +pub enum Node { + Internal { + height: u8, + summary: T::Summary, + child_summaries: ArrayVec, + child_trees: ArrayVec, { 2 * TREE_BASE }>, + }, + Leaf { + summary: T::Summary, + items: ArrayVec, + item_summaries: ArrayVec, + }, +} + +impl fmt::Debug for Node +where + T: Item + fmt::Debug, + T::Summary: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Node::Internal { + height, + summary, + child_summaries, + child_trees, + } => f + .debug_struct("Internal") + .field("height", height) + .field("summary", summary) + .field("child_summaries", child_summaries) + .field("child_trees", child_trees) + .finish(), + Node::Leaf { + summary, + items, + item_summaries, + } => f + .debug_struct("Leaf") + .field("summary", summary) + .field("items", items) + .field("item_summaries", item_summaries) + .finish(), + } + } +} + +impl Node { + fn is_leaf(&self) -> bool { + matches!(self, Node::Leaf { .. }) + } + + fn height(&self) -> u8 { + match self { + Node::Internal { height, .. } => *height, + Node::Leaf { .. } => 0, + } + } + + fn summary(&self) -> &T::Summary { + match self { + Node::Internal { summary, .. } => summary, + Node::Leaf { summary, .. } => summary, + } + } + + fn child_summaries(&self) -> &[T::Summary] { + match self { + Node::Internal { + child_summaries, .. + } => child_summaries.as_slice(), + Node::Leaf { item_summaries, .. } => item_summaries.as_slice(), + } + } + + fn child_trees(&self) -> &ArrayVec, { 2 * TREE_BASE }> { + match self { + Node::Internal { child_trees, .. } => child_trees, + Node::Leaf { .. } => panic!("Leaf nodes have no child trees"), + } + } + + fn items(&self) -> &ArrayVec { + match self { + Node::Leaf { items, .. } => items, + Node::Internal { .. } => panic!("Internal nodes have no items"), + } + } + + fn is_underflowing(&self) -> bool { + match self { + Node::Internal { child_trees, .. } => child_trees.len() < TREE_BASE, + Node::Leaf { items, .. } => items.len() < TREE_BASE, + } + } +} + +#[derive(Debug)] +pub enum Edit { + Insert(T), + Remove(T::Key), +} + +impl Edit { + fn key(&self) -> T::Key { + match self { + Edit::Insert(item) => item.key(), + Edit::Remove(key) => key.clone(), + } + } +} + +fn sum<'a, T, I>(iter: I, cx: T::Context<'_>) -> T +where + T: 'a + Summary, + I: Iterator, +{ + let mut sum = T::zero(cx); + for value in iter { + sum.add_summary(value, cx); + } + sum +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::{distr::StandardUniform, prelude::*}; + use std::cmp; + + #[ctor::ctor] + fn init_logger() { + zlog::init_test(); + } + + #[test] + fn test_extend_and_push_tree() { + let mut tree1 = SumTree::default(); + tree1.extend(0..20, ()); + + let mut tree2 = SumTree::default(); + tree2.extend(50..100, ()); + + tree1.append(tree2, ()); + assert_eq!(tree1.items(()), (0..20).chain(50..100).collect::>()); + } + + #[test] + fn test_random() { + let mut starting_seed = 0; + if let Ok(value) = std::env::var("SEED") { + starting_seed = value.parse().expect("invalid SEED variable"); + } + let mut num_iterations = 100; + if let Ok(value) = std::env::var("ITERATIONS") { + num_iterations = value.parse().expect("invalid ITERATIONS variable"); + } + let num_operations = std::env::var("OPERATIONS") + .map_or(5, |o| o.parse().expect("invalid OPERATIONS variable")); + + for seed in starting_seed..(starting_seed + num_iterations) { + eprintln!("seed = {}", seed); + let mut rng = StdRng::seed_from_u64(seed); + + let rng = &mut rng; + let mut tree = SumTree::::default(); + let count = rng.random_range(0..10); + if rng.random() { + tree.extend(rng.sample_iter(StandardUniform).take(count), ()); + } else { + let items = rng + .sample_iter(StandardUniform) + .take(count) + .collect::>(); + tree.par_extend(items, ()); + } + + for _ in 0..num_operations { + let splice_end = rng.random_range(0..tree.extent::(()).0 + 1); + let splice_start = rng.random_range(0..splice_end + 1); + let count = rng.random_range(0..10); + let tree_end = tree.extent::(()); + let new_items = rng + .sample_iter(StandardUniform) + .take(count) + .collect::>(); + + let mut reference_items = tree.items(()); + reference_items.splice(splice_start..splice_end, new_items.clone()); + + tree = { + let mut cursor = tree.cursor::(()); + let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right); + if rng.random() { + new_tree.extend(new_items, ()); + } else { + new_tree.par_extend(new_items, ()); + } + cursor.seek(&Count(splice_end), Bias::Right); + new_tree.append(cursor.slice(&tree_end, Bias::Right), ()); + new_tree + }; + + assert_eq!(tree.items(()), reference_items); + assert_eq!( + tree.iter().collect::>(), + tree.cursor::<()>(()).collect::>() + ); + + log::info!("tree items: {:?}", tree.items(())); + + let mut filter_cursor = + tree.filter::<_, Count>((), |summary| summary.contains_even); + let expected_filtered_items = tree + .items(()) + .into_iter() + .enumerate() + .filter(|(_, item)| (item & 1) == 0) + .collect::>(); + + let mut item_ix = if rng.random() { + filter_cursor.next(); + 0 + } else { + filter_cursor.prev(); + expected_filtered_items.len().saturating_sub(1) + }; + while item_ix < expected_filtered_items.len() { + log::info!("filter_cursor, item_ix: {}", item_ix); + let actual_item = filter_cursor.item().unwrap(); + let (reference_index, reference_item) = expected_filtered_items[item_ix]; + assert_eq!(actual_item, &reference_item); + assert_eq!(filter_cursor.start().0, reference_index); + log::info!("next"); + filter_cursor.next(); + item_ix += 1; + + while item_ix > 0 && rng.random_bool(0.2) { + log::info!("prev"); + filter_cursor.prev(); + item_ix -= 1; + + if item_ix == 0 && rng.random_bool(0.2) { + filter_cursor.prev(); + assert_eq!(filter_cursor.item(), None); + assert_eq!(filter_cursor.start().0, 0); + filter_cursor.next(); + } + } + } + assert_eq!(filter_cursor.item(), None); + + let mut before_start = false; + let mut cursor = tree.cursor::(()); + let start_pos = rng.random_range(0..=reference_items.len()); + cursor.seek(&Count(start_pos), Bias::Right); + let mut pos = rng.random_range(start_pos..=reference_items.len()); + cursor.seek_forward(&Count(pos), Bias::Right); + + for i in 0..10 { + assert_eq!(cursor.start().0, pos); + + if pos > 0 { + assert_eq!(cursor.prev_item().unwrap(), &reference_items[pos - 1]); + } else { + assert_eq!(cursor.prev_item(), None); + } + + if pos < reference_items.len() && !before_start { + assert_eq!(cursor.item().unwrap(), &reference_items[pos]); + } else { + assert_eq!(cursor.item(), None); + } + + if before_start { + assert_eq!(cursor.next_item(), reference_items.first()); + } else if pos + 1 < reference_items.len() { + assert_eq!(cursor.next_item().unwrap(), &reference_items[pos + 1]); + } else { + assert_eq!(cursor.next_item(), None); + } + + if i < 5 { + cursor.next(); + if pos < reference_items.len() { + pos += 1; + before_start = false; + } + } else { + cursor.prev(); + if pos == 0 { + before_start = true; + } + pos = pos.saturating_sub(1); + } + } + } + + for _ in 0..10 { + let end = rng.random_range(0..tree.extent::(()).0 + 1); + let start = rng.random_range(0..end + 1); + let start_bias = if rng.random() { + Bias::Left + } else { + Bias::Right + }; + let end_bias = if rng.random() { + Bias::Left + } else { + Bias::Right + }; + + let mut cursor = tree.cursor::(()); + cursor.seek(&Count(start), start_bias); + let slice = cursor.slice(&Count(end), end_bias); + + cursor.seek(&Count(start), start_bias); + let summary = cursor.summary::<_, Sum>(&Count(end), end_bias); + + assert_eq!(summary.0, slice.summary().sum); + } + } + } + + #[test] + fn test_cursor() { + // Empty tree + let tree = SumTree::::default(); + let mut cursor = tree.cursor::(()); + assert_eq!( + cursor.slice(&Count(0), Bias::Right).items(()), + Vec::::new() + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + cursor.prev(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + cursor.next(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + + // Single-element tree + let mut tree = SumTree::::default(); + tree.extend(vec![1], ()); + let mut cursor = tree.cursor::(()); + assert_eq!( + cursor.slice(&Count(0), Bias::Right).items(()), + Vec::::new() + ); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + + cursor.next(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 1); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + + let mut cursor = tree.cursor::(()); + assert_eq!(cursor.slice(&Count(1), Bias::Right).items(()), [1]); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 1); + + cursor.seek(&Count(0), Bias::Right); + assert_eq!( + cursor + .slice(&tree.extent::(()), Bias::Right) + .items(()), + [1] + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 1); + + // Multiple-element tree + let mut tree = SumTree::default(); + tree.extend(vec![1, 2, 3, 4, 5, 6], ()); + let mut cursor = tree.cursor::(()); + + assert_eq!(cursor.slice(&Count(2), Bias::Right).items(()), [1, 2]); + assert_eq!(cursor.item(), Some(&3)); + assert_eq!(cursor.prev_item(), Some(&2)); + assert_eq!(cursor.next_item(), Some(&4)); + assert_eq!(cursor.start().sum, 3); + + cursor.next(); + assert_eq!(cursor.item(), Some(&4)); + assert_eq!(cursor.prev_item(), Some(&3)); + assert_eq!(cursor.next_item(), Some(&5)); + assert_eq!(cursor.start().sum, 6); + + cursor.next(); + assert_eq!(cursor.item(), Some(&5)); + assert_eq!(cursor.prev_item(), Some(&4)); + assert_eq!(cursor.next_item(), Some(&6)); + assert_eq!(cursor.start().sum, 10); + + cursor.next(); + assert_eq!(cursor.item(), Some(&6)); + assert_eq!(cursor.prev_item(), Some(&5)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 15); + + cursor.next(); + cursor.next(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&6)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 21); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&6)); + assert_eq!(cursor.prev_item(), Some(&5)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 15); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&5)); + assert_eq!(cursor.prev_item(), Some(&4)); + assert_eq!(cursor.next_item(), Some(&6)); + assert_eq!(cursor.start().sum, 10); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&4)); + assert_eq!(cursor.prev_item(), Some(&3)); + assert_eq!(cursor.next_item(), Some(&5)); + assert_eq!(cursor.start().sum, 6); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&3)); + assert_eq!(cursor.prev_item(), Some(&2)); + assert_eq!(cursor.next_item(), Some(&4)); + assert_eq!(cursor.start().sum, 3); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&2)); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), Some(&3)); + assert_eq!(cursor.start().sum, 1); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), Some(&2)); + assert_eq!(cursor.start().sum, 0); + + cursor.prev(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), Some(&1)); + assert_eq!(cursor.start().sum, 0); + + cursor.next(); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), Some(&2)); + assert_eq!(cursor.start().sum, 0); + + let mut cursor = tree.cursor::(()); + assert_eq!( + cursor + .slice(&tree.extent::(()), Bias::Right) + .items(()), + tree.items(()) + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&6)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 21); + + cursor.seek(&Count(3), Bias::Right); + assert_eq!( + cursor + .slice(&tree.extent::(()), Bias::Right) + .items(()), + [4, 5, 6] + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&6)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 21); + + // Seeking can bias left or right + cursor.seek(&Count(1), Bias::Left); + assert_eq!(cursor.item(), Some(&1)); + cursor.seek(&Count(1), Bias::Right); + assert_eq!(cursor.item(), Some(&2)); + + // Slicing without resetting starts from where the cursor is parked at. + cursor.seek(&Count(1), Bias::Right); + assert_eq!(cursor.slice(&Count(3), Bias::Right).items(()), vec![2, 3]); + assert_eq!(cursor.slice(&Count(6), Bias::Left).items(()), vec![4, 5]); + assert_eq!(cursor.slice(&Count(6), Bias::Right).items(()), vec![6]); + } + + #[test] + fn test_edit() { + let mut tree = SumTree::::default(); + + let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)], ()); + assert_eq!(tree.items(()), vec![0, 1, 2]); + assert_eq!(removed, Vec::::new()); + assert_eq!(tree.get(&0, ()), Some(&0)); + assert_eq!(tree.get(&1, ()), Some(&1)); + assert_eq!(tree.get(&2, ()), Some(&2)); + assert_eq!(tree.get(&4, ()), None); + + let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)], ()); + assert_eq!(tree.items(()), vec![1, 2, 4]); + assert_eq!(removed, vec![0, 2]); + assert_eq!(tree.get(&0, ()), None); + assert_eq!(tree.get(&1, ()), Some(&1)); + assert_eq!(tree.get(&2, ()), Some(&2)); + assert_eq!(tree.get(&4, ()), Some(&4)); + } + + #[test] + fn test_from_iter() { + assert_eq!( + SumTree::from_iter(0..100, ()).items(()), + (0..100).collect::>() + ); + + // Ensure `from_iter` works correctly when the given iterator restarts + // after calling `next` if `None` was already returned. + let mut ix = 0; + let iterator = std::iter::from_fn(|| { + ix = (ix + 1) % 2; + if ix == 1 { Some(1) } else { None } + }); + assert_eq!(SumTree::from_iter(iterator, ()).items(()), vec![1]); + } + + #[derive(Clone, Default, Debug)] + pub struct IntegersSummary { + count: usize, + sum: usize, + contains_even: bool, + max: u8, + } + + #[derive(Ord, PartialOrd, Default, Eq, PartialEq, Clone, Debug)] + struct Count(usize); + + #[derive(Ord, PartialOrd, Default, Eq, PartialEq, Clone, Debug)] + struct Sum(usize); + + impl Item for u8 { + type Summary = IntegersSummary; + + fn summary(&self, _cx: ()) -> Self::Summary { + IntegersSummary { + count: 1, + sum: *self as usize, + contains_even: (*self & 1) == 0, + max: *self, + } + } + } + + impl KeyedItem for u8 { + type Key = u8; + + fn key(&self) -> Self::Key { + *self + } + } + + impl ContextLessSummary for IntegersSummary { + fn zero() -> Self { + Default::default() + } + + fn add_summary(&mut self, other: &Self) { + self.count += other.count; + self.sum += other.sum; + self.contains_even |= other.contains_even; + self.max = cmp::max(self.max, other.max); + } + } + + impl Dimension<'_, IntegersSummary> for u8 { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &IntegersSummary, _: ()) { + *self = summary.max; + } + } + + impl Dimension<'_, IntegersSummary> for Count { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &IntegersSummary, _: ()) { + self.0 += summary.count; + } + } + + impl SeekTarget<'_, IntegersSummary, IntegersSummary> for Count { + fn cmp(&self, cursor_location: &IntegersSummary, _: ()) -> Ordering { + self.0.cmp(&cursor_location.count) + } + } + + impl Dimension<'_, IntegersSummary> for Sum { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &IntegersSummary, _: ()) { + self.0 += summary.sum; + } + } +} diff --git a/third_party/gpui_support/gpui_sum_tree/src/tree_map.rs b/third_party/gpui_support/gpui_sum_tree/src/tree_map.rs new file mode 100644 index 0000000..3e56194 --- /dev/null +++ b/third_party/gpui_support/gpui_sum_tree/src/tree_map.rs @@ -0,0 +1,521 @@ +use std::{cmp::Ordering, fmt::Debug}; + +use crate::{Bias, ContextLessSummary, Dimension, Edit, Item, KeyedItem, SeekTarget, SumTree}; + +/// A cheaply-cloneable ordered map based on a [SumTree](crate::SumTree). +#[derive(Clone, PartialEq, Eq)] +pub struct TreeMap(SumTree>) +where + K: Clone + Ord, + V: Clone; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MapEntry { + key: K, + value: V, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct MapKey(Option); + +impl Default for MapKey { + fn default() -> Self { + Self(None) + } +} + +#[derive(Clone, Debug)] +pub struct MapKeyRef<'a, K>(Option<&'a K>); + +impl Default for MapKeyRef<'_, K> { + fn default() -> Self { + Self(None) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TreeSet(TreeMap) +where + K: Clone + Ord; + +impl TreeMap { + pub fn from_ordered_entries(entries: impl IntoIterator) -> Self { + let tree = SumTree::from_iter( + entries + .into_iter() + .map(|(key, value)| MapEntry { key, value }), + (), + ); + Self(tree) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn get(&self, key: &K) -> Option<&V> { + let (.., item) = self + .0 + .find::, _>((), &MapKeyRef(Some(key)), Bias::Left); + if let Some(item) = item { + if Some(key) == item.key().0.as_ref() { + Some(&item.value) + } else { + None + } + } else { + None + } + } + + pub fn insert(&mut self, key: K, value: V) { + self.0.insert_or_replace(MapEntry { key, value }, ()); + } + + pub fn extend(&mut self, iter: impl IntoIterator) { + let edits: Vec<_> = iter + .into_iter() + .map(|(key, value)| Edit::Insert(MapEntry { key, value })) + .collect(); + self.0.edit(edits, ()); + } + + pub fn clear(&mut self) { + self.0 = SumTree::default(); + } + + pub fn remove(&mut self, key: &K) -> Option { + let mut removed = None; + let mut cursor = self.0.cursor::>(()); + let key = MapKeyRef(Some(key)); + let mut new_tree = cursor.slice(&key, Bias::Left); + if key.cmp(&cursor.end(), ()) == Ordering::Equal { + removed = Some(cursor.item().unwrap().value.clone()); + cursor.next(); + } + new_tree.append(cursor.suffix(), ()); + drop(cursor); + self.0 = new_tree; + removed + } + + pub fn remove_range(&mut self, start: &impl MapSeekTarget, end: &impl MapSeekTarget) { + let start = MapSeekTargetAdaptor(start); + let end = MapSeekTargetAdaptor(end); + let mut cursor = self.0.cursor::>(()); + let mut new_tree = cursor.slice(&start, Bias::Left); + cursor.seek(&end, Bias::Left); + new_tree.append(cursor.suffix(), ()); + drop(cursor); + self.0 = new_tree; + } + + /// Returns the key-value pair with the greatest key less than or equal to the given key. + pub fn closest(&self, key: &K) -> Option<(&K, &V)> { + let mut cursor = self.0.cursor::>(()); + let key = MapKeyRef(Some(key)); + cursor.seek(&key, Bias::Right); + cursor.prev(); + cursor.item().map(|item| (&item.key, &item.value)) + } + + pub fn iter_from<'a>(&'a self, from: &K) -> impl Iterator + 'a { + let mut cursor = self.0.cursor::>(()); + let from_key = MapKeyRef(Some(from)); + cursor.seek(&from_key, Bias::Left); + + cursor.map(|map_entry| (&map_entry.key, &map_entry.value)) + } + + pub fn update(&mut self, key: &K, f: F) -> Option + where + F: FnOnce(&mut V) -> T, + { + let mut cursor = self.0.cursor::>(()); + let key = MapKeyRef(Some(key)); + let mut new_tree = cursor.slice(&key, Bias::Left); + let mut result = None; + if key.cmp(&cursor.end(), ()) == Ordering::Equal { + let mut updated = cursor.item().unwrap().clone(); + result = Some(f(&mut updated.value)); + new_tree.push(updated, ()); + cursor.next(); + } + new_tree.append(cursor.suffix(), ()); + drop(cursor); + self.0 = new_tree; + result + } + + pub fn retain bool>(&mut self, mut predicate: F) { + let mut new_map = SumTree::>::default(); + + let mut cursor = self.0.cursor::>(()); + cursor.next(); + while let Some(item) = cursor.item() { + if predicate(&item.key, &item.value) { + new_map.push(item.clone(), ()); + } + cursor.next(); + } + drop(cursor); + + self.0 = new_map; + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().map(|entry| (&entry.key, &entry.value)) + } + + pub fn values(&self) -> impl Iterator + '_ { + self.0.iter().map(|entry| &entry.value) + } + + pub fn first(&self) -> Option<(&K, &V)> { + self.0.first().map(|entry| (&entry.key, &entry.value)) + } + + pub fn last(&self) -> Option<(&K, &V)> { + self.0.last().map(|entry| (&entry.key, &entry.value)) + } + + pub fn insert_tree(&mut self, other: TreeMap) { + let edits = other + .iter() + .map(|(key, value)| { + Edit::Insert(MapEntry { + key: key.to_owned(), + value: value.to_owned(), + }) + }) + .collect(); + + self.0.edit(edits, ()); + } +} + +impl Debug for TreeMap +where + K: Clone + Debug + Ord, + V: Clone + Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +#[derive(Debug)] +struct MapSeekTargetAdaptor<'a, T>(&'a T); + +impl<'a, K: Clone + Ord, T: MapSeekTarget> SeekTarget<'a, MapKey, MapKeyRef<'a, K>> + for MapSeekTargetAdaptor<'_, T> +{ + fn cmp(&self, cursor_location: &MapKeyRef, _: ()) -> Ordering { + if let Some(key) = &cursor_location.0 { + MapSeekTarget::cmp_cursor(self.0, key) + } else { + Ordering::Greater + } + } +} + +pub trait MapSeekTarget { + fn cmp_cursor(&self, cursor_location: &K) -> Ordering; +} + +impl MapSeekTarget for K { + fn cmp_cursor(&self, cursor_location: &K) -> Ordering { + self.cmp(cursor_location) + } +} + +impl Default for TreeMap +where + K: Clone + Ord, + V: Clone, +{ + fn default() -> Self { + Self(Default::default()) + } +} + +impl Item for MapEntry +where + K: Clone + Ord, + V: Clone, +{ + type Summary = MapKey; + + fn summary(&self, _cx: ()) -> Self::Summary { + self.key() + } +} + +impl KeyedItem for MapEntry +where + K: Clone + Ord, + V: Clone, +{ + type Key = MapKey; + + fn key(&self) -> Self::Key { + MapKey(Some(self.key.clone())) + } +} + +impl ContextLessSummary for MapKey +where + K: Clone, +{ + fn zero() -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &Self) { + *self = summary.clone() + } +} + +impl<'a, K> Dimension<'a, MapKey> for MapKeyRef<'a, K> +where + K: Clone + Ord, +{ + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &'a MapKey, _: ()) { + self.0 = summary.0.as_ref(); + } +} + +impl<'a, K> SeekTarget<'a, MapKey, MapKeyRef<'a, K>> for MapKeyRef<'_, K> +where + K: Clone + Ord, +{ + fn cmp(&self, cursor_location: &MapKeyRef, _: ()) -> Ordering { + Ord::cmp(&self.0, &cursor_location.0) + } +} + +impl Default for TreeSet +where + K: Clone + Ord, +{ + fn default() -> Self { + Self(Default::default()) + } +} + +impl TreeSet +where + K: Clone + Ord, +{ + pub fn from_ordered_entries(entries: impl IntoIterator) -> Self { + Self(TreeMap::from_ordered_entries( + entries.into_iter().map(|key| (key, ())), + )) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn insert(&mut self, key: K) { + self.0.insert(key, ()); + } + + pub fn remove(&mut self, key: &K) -> bool { + self.0.remove(key).is_some() + } + + pub fn extend(&mut self, iter: impl IntoIterator) { + self.0.extend(iter.into_iter().map(|key| (key, ()))); + } + + pub fn contains(&self, key: &K) -> bool { + self.0.get(key).is_some() + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().map(|(k, _)| k) + } + + pub fn iter_from<'a>(&'a self, key: &K) -> impl Iterator + 'a { + self.0.iter_from(key).map(move |(k, _)| k) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic() { + let mut map = TreeMap::default(); + assert_eq!(map.iter().collect::>(), vec![]); + + map.insert(3, "c"); + assert_eq!(map.get(&3), Some(&"c")); + assert_eq!(map.iter().collect::>(), vec![(&3, &"c")]); + + map.insert(1, "a"); + assert_eq!(map.get(&1), Some(&"a")); + assert_eq!(map.iter().collect::>(), vec![(&1, &"a"), (&3, &"c")]); + + map.insert(2, "b"); + assert_eq!(map.get(&2), Some(&"b")); + assert_eq!(map.get(&1), Some(&"a")); + assert_eq!(map.get(&3), Some(&"c")); + assert_eq!( + map.iter().collect::>(), + vec![(&1, &"a"), (&2, &"b"), (&3, &"c")] + ); + + assert_eq!(map.closest(&0), None); + assert_eq!(map.closest(&1), Some((&1, &"a"))); + assert_eq!(map.closest(&10), Some((&3, &"c"))); + + map.remove(&2); + assert_eq!(map.get(&2), None); + assert_eq!(map.iter().collect::>(), vec![(&1, &"a"), (&3, &"c")]); + + assert_eq!(map.closest(&2), Some((&1, &"a"))); + + map.remove(&3); + assert_eq!(map.get(&3), None); + assert_eq!(map.iter().collect::>(), vec![(&1, &"a")]); + + map.remove(&1); + assert_eq!(map.get(&1), None); + assert_eq!(map.iter().collect::>(), vec![]); + + map.insert(4, "d"); + map.insert(5, "e"); + map.insert(6, "f"); + map.retain(|key, _| *key % 2 == 0); + assert_eq!(map.iter().collect::>(), vec![(&4, &"d"), (&6, &"f")]); + } + + #[test] + fn test_iter_from() { + let mut map = TreeMap::default(); + + map.insert("a", 1); + map.insert("b", 2); + map.insert("baa", 3); + map.insert("baaab", 4); + map.insert("c", 5); + + let result = map + .iter_from(&"ba") + .take_while(|(key, _)| key.starts_with("ba")) + .collect::>(); + + assert_eq!(result.len(), 2); + assert!(result.iter().any(|(k, _)| k == &&"baa")); + assert!(result.iter().any(|(k, _)| k == &&"baaab")); + + let result = map + .iter_from(&"c") + .take_while(|(key, _)| key.starts_with("c")) + .collect::>(); + + assert_eq!(result.len(), 1); + assert!(result.iter().any(|(k, _)| k == &&"c")); + } + + #[test] + fn test_insert_tree() { + let mut map = TreeMap::default(); + map.insert("a", 1); + map.insert("b", 2); + map.insert("c", 3); + + let mut other = TreeMap::default(); + other.insert("a", 2); + other.insert("b", 2); + other.insert("d", 4); + + map.insert_tree(other); + + assert_eq!(map.iter().count(), 4); + assert_eq!(map.get(&"a"), Some(&2)); + assert_eq!(map.get(&"b"), Some(&2)); + assert_eq!(map.get(&"c"), Some(&3)); + assert_eq!(map.get(&"d"), Some(&4)); + } + + #[test] + fn test_extend() { + let mut map = TreeMap::default(); + map.insert("a", 1); + map.insert("b", 2); + map.insert("c", 3); + map.extend([("a", 2), ("b", 2), ("d", 4)]); + assert_eq!(map.iter().count(), 4); + assert_eq!(map.get(&"a"), Some(&2)); + assert_eq!(map.get(&"b"), Some(&2)); + assert_eq!(map.get(&"c"), Some(&3)); + assert_eq!(map.get(&"d"), Some(&4)); + } + + #[test] + fn test_remove_between_and_path_successor() { + use std::path::{Path, PathBuf}; + + #[derive(Debug)] + pub struct PathDescendants<'a>(&'a Path); + + impl MapSeekTarget for PathDescendants<'_> { + fn cmp_cursor(&self, key: &PathBuf) -> Ordering { + if key.starts_with(self.0) { + Ordering::Greater + } else { + self.0.cmp(key) + } + } + } + + let mut map = TreeMap::default(); + + map.insert(PathBuf::from("a"), 1); + map.insert(PathBuf::from("a/a"), 1); + map.insert(PathBuf::from("b"), 2); + map.insert(PathBuf::from("b/a/a"), 3); + map.insert(PathBuf::from("b/a/a/a/b"), 4); + map.insert(PathBuf::from("c"), 5); + map.insert(PathBuf::from("c/a"), 6); + + map.remove_range( + &PathBuf::from("b/a"), + &PathDescendants(&PathBuf::from("b/a")), + ); + + assert_eq!(map.get(&PathBuf::from("a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("a/a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("b")), Some(&2)); + assert_eq!(map.get(&PathBuf::from("b/a/a")), None); + assert_eq!(map.get(&PathBuf::from("b/a/a/a/b")), None); + assert_eq!(map.get(&PathBuf::from("c")), Some(&5)); + assert_eq!(map.get(&PathBuf::from("c/a")), Some(&6)); + + map.remove_range(&PathBuf::from("c"), &PathDescendants(&PathBuf::from("c"))); + + assert_eq!(map.get(&PathBuf::from("a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("a/a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("b")), Some(&2)); + assert_eq!(map.get(&PathBuf::from("c")), None); + assert_eq!(map.get(&PathBuf::from("c/a")), None); + + map.remove_range(&PathBuf::from("a"), &PathDescendants(&PathBuf::from("a"))); + + assert_eq!(map.get(&PathBuf::from("a")), None); + assert_eq!(map.get(&PathBuf::from("a/a")), None); + assert_eq!(map.get(&PathBuf::from("b")), Some(&2)); + + map.remove_range(&PathBuf::from("b"), &PathDescendants(&PathBuf::from("b"))); + + assert_eq!(map.get(&PathBuf::from("b")), None); + } +} diff --git a/third_party/gpui_support/gpui_util/Cargo.toml b/third_party/gpui_support/gpui_util/Cargo.toml new file mode 100644 index 0000000..94da83a --- /dev/null +++ b/third_party/gpui_support/gpui_util/Cargo.toml @@ -0,0 +1,194 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# Local copy keeps the registry manifest shape and narrows log features so +# GPUI and Servo can share the same workspace dependency graph. + +[package] +edition = "2024" +name = "gpui_util" +version = "0.2.2" +build = false +publish = true +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A collection of utility structs and functions used by Zed and GPUI" +readme = false +license = "Apache-2.0" +resolver = "2" + +[features] +test-support = [ + "git2", + "rand", + "util_macros", +] + +[lib] +name = "gpui_util" +path = "src/util.rs" +doctest = true + +[dependencies.anyhow] +version = "1.0.86" + +[dependencies.async-fs] +version = "2.1" + +[dependencies.async_zip] +version = "0.0.17" +features = [ + "deflate", + "deflate64", +] + +[dependencies.collections] +version = "0.2.2" +package = "gpui_collections" + +[dependencies.dirs] +version = "4.0" + +[dependencies.dunce] +version = "1.0" + +[dependencies.futures] +version = "0.3" + +[dependencies.futures-lite] +version = "1.13" + +[dependencies.git2] +version = "0.20.1" +optional = true +default-features = false + +[dependencies.globset] +version = "0.4" + +[dependencies.itertools] +version = "0.14.0" + +[dependencies.log] +version = "0.4.16" + +[dependencies.rand] +version = "0.9" +optional = true + +[dependencies.regex] +version = "1.5" + +[dependencies.rust-embed] +version = "8.4" +features = ["include-exclude"] + +[dependencies.schemars] +version = "1.0" +features = ["indexmap2"] + +[dependencies.serde] +version = "1.0.221" +features = [ + "derive", + "rc", +] + +[dependencies.serde_json] +version = "1.0.144" +features = [ + "preserve_order", + "raw_value", +] + +[dependencies.serde_json_lenient] +version = "0.2" +features = [ + "preserve_order", + "raw_value", +] + +[dependencies.shlex] +version = "1.3.0" + +[dependencies.smol] +version = "2.0" + +[dependencies.take-until] +version = "0.2.0" + +[dependencies.tempfile] +version = "3.20.0" + +[dependencies.unicase] +version = "2.6" + +[dependencies.util_macros] +version = "0.2.2" +optional = true +package = "gpui_util_macros" + +[dependencies.walkdir] +version = "2.5" + +[dependencies.which] +version = "6.0.0" + +[dev-dependencies.git2] +version = "0.20.1" +default-features = false + +[dev-dependencies.indoc] +version = "2" + +[dev-dependencies.pretty_assertions] +version = "1.3.0" +features = ["unstable"] + +[dev-dependencies.rand] +version = "0.9" + +[dev-dependencies.util_macros] +version = "0.2.2" +package = "gpui_util_macros" + +[target."cfg(unix)".dependencies.command-fds] +version = "0.3.1" + +[target."cfg(unix)".dependencies.libc] +version = "0.2" + +[target."cfg(unix)".dependencies.nix] +version = "0.29" +features = ["user"] + +[target."cfg(windows)".dependencies.tendril] +version = "0.4.3" + +[lints.clippy] +dbg_macro = "deny" +declare_interior_mutable_const = "deny" +disallowed_methods = "deny" +large_enum_variant = "allow" +let_underscore_future = "allow" +nonminimal_bool = "allow" +redundant_clone = "deny" +single_range_in_vec_init = "allow" +todo = "deny" +too_many_arguments = "allow" +type_complexity = "allow" + +[lints.clippy.style] +level = "allow" +priority = -1 + +[lints.rust.unexpected_cfgs] +level = "allow" +priority = 0 diff --git a/third_party/gpui_support/gpui_util/LICENSE-APACHE b/third_party/gpui_support/gpui_util/LICENSE-APACHE new file mode 100644 index 0000000..461a0fe --- /dev/null +++ b/third_party/gpui_support/gpui_util/LICENSE-APACHE @@ -0,0 +1,222 @@ +Copyright 2022 - 2025 Zed Industries, Inc. + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + + 1. Definitions. + + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + END OF TERMS AND CONDITIONS diff --git a/third_party/gpui_support/gpui_util/src/arc_cow.rs b/third_party/gpui_support/gpui_util/src/arc_cow.rs new file mode 100644 index 0000000..4104067 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/arc_cow.rs @@ -0,0 +1,141 @@ +use std::{ + borrow::Cow, + cmp::Ordering, + fmt::{self, Debug}, + hash::{Hash, Hasher}, + sync::Arc, +}; + +pub enum ArcCow<'a, T: ?Sized> { + Borrowed(&'a T), + Owned(Arc), +} + +impl PartialEq for ArcCow<'_, T> { + fn eq(&self, other: &Self) -> bool { + let a = self.as_ref(); + let b = other.as_ref(); + a == b + } +} + +impl PartialOrd for ArcCow<'_, T> { + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ref().partial_cmp(other.as_ref()) + } +} + +impl Ord for ArcCow<'_, T> { + fn cmp(&self, other: &Self) -> Ordering { + self.as_ref().cmp(other.as_ref()) + } +} + +impl Eq for ArcCow<'_, T> {} + +impl Hash for ArcCow<'_, T> { + fn hash(&self, state: &mut H) { + match self { + Self::Borrowed(borrowed) => Hash::hash(borrowed, state), + Self::Owned(owned) => Hash::hash(&**owned, state), + } + } +} + +impl Clone for ArcCow<'_, T> { + fn clone(&self) -> Self { + match self { + Self::Borrowed(borrowed) => Self::Borrowed(borrowed), + Self::Owned(owned) => Self::Owned(owned.clone()), + } + } +} + +impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> { + fn from(s: &'a T) -> Self { + Self::Borrowed(s) + } +} + +impl From> for ArcCow<'_, T> { + fn from(s: Arc) -> Self { + Self::Owned(s) + } +} + +impl From<&'_ Arc> for ArcCow<'_, T> { + fn from(s: &'_ Arc) -> Self { + Self::Owned(s.clone()) + } +} + +impl From for ArcCow<'_, str> { + fn from(value: String) -> Self { + Self::Owned(value.into()) + } +} + +impl From<&String> for ArcCow<'_, str> { + fn from(value: &String) -> Self { + Self::Owned(value.clone().into()) + } +} + +impl<'a> From> for ArcCow<'a, str> { + fn from(value: Cow<'a, str>) -> Self { + match value { + Cow::Borrowed(borrowed) => Self::Borrowed(borrowed), + Cow::Owned(owned) => Self::Owned(owned.into()), + } + } +} + +impl From> for ArcCow<'_, [T]> { + fn from(vec: Vec) -> Self { + ArcCow::Owned(Arc::from(vec)) + } +} + +impl<'a> From<&'a str> for ArcCow<'a, [u8]> { + fn from(s: &'a str) -> Self { + ArcCow::Borrowed(s.as_bytes()) + } +} + +impl std::borrow::Borrow for ArcCow<'_, T> { + fn borrow(&self) -> &T { + match self { + ArcCow::Borrowed(borrowed) => borrowed, + ArcCow::Owned(owned) => owned.as_ref(), + } + } +} + +impl std::ops::Deref for ArcCow<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + ArcCow::Borrowed(s) => s, + ArcCow::Owned(s) => s.as_ref(), + } + } +} + +impl AsRef for ArcCow<'_, T> { + fn as_ref(&self) -> &T { + match self { + ArcCow::Borrowed(borrowed) => borrowed, + ArcCow::Owned(owned) => owned.as_ref(), + } + } +} + +impl Debug for ArcCow<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ArcCow::Borrowed(borrowed) => Debug::fmt(borrowed, f), + ArcCow::Owned(owned) => Debug::fmt(&**owned, f), + } + } +} diff --git a/third_party/gpui_support/gpui_util/src/archive.rs b/third_party/gpui_support/gpui_util/src/archive.rs new file mode 100644 index 0000000..9b58b16 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/archive.rs @@ -0,0 +1,253 @@ +use std::path::Path; + +use anyhow::{Context as _, Result}; +use async_zip::base::read; +#[cfg(not(windows))] +use futures::AsyncSeek; +use futures::{AsyncRead, io::BufReader}; + +#[cfg(windows)] +pub async fn extract_zip(destination: &Path, reader: R) -> Result<()> { + let mut reader = read::stream::ZipFileReader::new(BufReader::new(reader)); + + let destination = &destination + .canonicalize() + .unwrap_or_else(|_| destination.to_path_buf()); + + while let Some(mut item) = reader.next_with_entry().await? { + let entry_reader = item.reader_mut(); + let entry = entry_reader.entry(); + let path = destination.join( + entry + .filename() + .as_str() + .context("reading zip entry file name")?, + ); + + if entry + .dir() + .with_context(|| format!("reading zip entry metadata for path {path:?}"))? + { + std::fs::create_dir_all(&path) + .with_context(|| format!("creating directory {path:?}"))?; + } else { + let parent_dir = path + .parent() + .with_context(|| format!("no parent directory for {path:?}"))?; + std::fs::create_dir_all(parent_dir) + .with_context(|| format!("creating parent directory {parent_dir:?}"))?; + let mut file = smol::fs::File::create(&path) + .await + .with_context(|| format!("creating file {path:?}"))?; + futures::io::copy(entry_reader, &mut file) + .await + .with_context(|| format!("extracting into file {path:?}"))?; + } + + reader = item.skip().await.context("reading next zip entry")?; + } + + Ok(()) +} + +#[cfg(not(windows))] +pub async fn extract_zip(destination: &Path, reader: R) -> Result<()> { + // Unix needs file permissions copied when extracting. + // This is only possible to do when a reader impls `AsyncSeek` and `seek::ZipFileReader` is used. + // `stream::ZipFileReader` also has the `unix_permissions` method, but it will always return `Some(0)`. + // + // A typical `reader` comes from a streaming network response, so cannot be sought right away, + // and reading the entire archive into the memory seems wasteful. + // + // So, save the stream into a temporary file first and then get it read with a seeking reader. + let mut file = async_fs::File::from(tempfile::tempfile().context("creating a temporary file")?); + futures::io::copy(&mut BufReader::new(reader), &mut file) + .await + .context("saving archive contents into the temporary file")?; + extract_seekable_zip(destination, file).await +} + +#[cfg(not(windows))] +pub async fn extract_seekable_zip( + destination: &Path, + reader: R, +) -> Result<()> { + let mut reader = read::seek::ZipFileReader::new(BufReader::new(reader)) + .await + .context("reading the zip archive")?; + let destination = &destination + .canonicalize() + .unwrap_or_else(|_| destination.to_path_buf()); + for (i, entry) in reader.file().entries().to_vec().into_iter().enumerate() { + let path = destination.join( + entry + .filename() + .as_str() + .context("reading zip entry file name")?, + ); + + if entry + .dir() + .with_context(|| format!("reading zip entry metadata for path {path:?}"))? + { + std::fs::create_dir_all(&path) + .with_context(|| format!("creating directory {path:?}"))?; + } else { + let parent_dir = path + .parent() + .with_context(|| format!("no parent directory for {path:?}"))?; + std::fs::create_dir_all(parent_dir) + .with_context(|| format!("creating parent directory {parent_dir:?}"))?; + let mut file = smol::fs::File::create(&path) + .await + .with_context(|| format!("creating file {path:?}"))?; + let mut entry_reader = reader + .reader_with_entry(i) + .await + .with_context(|| format!("reading entry for path {path:?}"))?; + futures::io::copy(&mut entry_reader, &mut file) + .await + .with_context(|| format!("extracting into file {path:?}"))?; + + if let Some(perms) = entry.unix_permissions() { + use std::os::unix::fs::PermissionsExt; + let permissions = std::fs::Permissions::from_mode(u32::from(perms)); + file.set_permissions(permissions) + .await + .with_context(|| format!("setting permissions for file {path:?}"))?; + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use async_zip::ZipEntryBuilder; + use async_zip::base::write::ZipFileWriter; + use futures::{AsyncSeek, AsyncWriteExt}; + use smol::io::Cursor; + use tempfile::TempDir; + + use super::*; + + async fn compress_zip(src_dir: &Path, dst: &Path) -> Result<()> { + let mut out = smol::fs::File::create(dst).await?; + let mut writer = ZipFileWriter::new(&mut out); + + for entry in walkdir::WalkDir::new(src_dir) { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + continue; + } + + let relative_path = path.strip_prefix(src_dir)?; + let data = smol::fs::read(&path).await?; + + let filename = relative_path.display().to_string(); + + #[cfg(unix)] + { + let mut builder = + ZipEntryBuilder::new(filename.into(), async_zip::Compression::Deflate); + use std::os::unix::fs::PermissionsExt; + let metadata = std::fs::metadata(path)?; + let perms = metadata.permissions().mode() as u16; + builder = builder.unix_permissions(perms); + writer.write_entry_whole(builder, &data).await?; + } + #[cfg(not(unix))] + { + let builder = + ZipEntryBuilder::new(filename.into(), async_zip::Compression::Deflate); + writer.write_entry_whole(builder, &data).await?; + } + } + + writer.close().await?; + out.flush().await?; + + Ok(()) + } + + #[track_caller] + fn assert_file_content(path: &Path, content: &str) { + assert!(path.exists(), "file not found: {:?}", path); + let actual = std::fs::read_to_string(path).unwrap(); + assert_eq!(actual, content); + } + + #[track_caller] + fn make_test_data() -> TempDir { + let dir = tempfile::tempdir().unwrap(); + let dst = dir.path(); + + std::fs::write(dst.join("test"), "Hello world.").unwrap(); + std::fs::create_dir_all(dst.join("foo/bar")).unwrap(); + std::fs::write(dst.join("foo/bar.txt"), "Foo bar.").unwrap(); + std::fs::write(dst.join("foo/dar.md"), "Bar dar.").unwrap(); + std::fs::write(dst.join("foo/bar/dar你好.txt"), "你好世界").unwrap(); + + dir + } + + async fn read_archive(path: &Path) -> impl AsyncRead + AsyncSeek + Unpin { + let data = smol::fs::read(&path).await.unwrap(); + Cursor::new(data) + } + + #[test] + fn test_extract_zip() { + let test_dir = make_test_data(); + let zip_file = test_dir.path().join("test.zip"); + + smol::block_on(async { + compress_zip(test_dir.path(), &zip_file).await.unwrap(); + let reader = read_archive(&zip_file).await; + + let dir = tempfile::tempdir().unwrap(); + let dst = dir.path(); + extract_zip(dst, reader).await.unwrap(); + + assert_file_content(&dst.join("test"), "Hello world."); + assert_file_content(&dst.join("foo/bar.txt"), "Foo bar."); + assert_file_content(&dst.join("foo/dar.md"), "Bar dar."); + assert_file_content(&dst.join("foo/bar/dar你好.txt"), "你好世界"); + }); + } + + #[cfg(unix)] + #[test] + fn test_extract_zip_preserves_executable_permissions() { + use std::os::unix::fs::PermissionsExt; + + smol::block_on(async { + let test_dir = tempfile::tempdir().unwrap(); + let executable_path = test_dir.path().join("my_script"); + + // Create an executable file + std::fs::write(&executable_path, "#!/bin/bash\necho 'Hello'").unwrap(); + let mut perms = std::fs::metadata(&executable_path).unwrap().permissions(); + perms.set_mode(0o755); // rwxr-xr-x + std::fs::set_permissions(&executable_path, perms).unwrap(); + + // Create zip + let zip_file = test_dir.path().join("test.zip"); + compress_zip(test_dir.path(), &zip_file).await.unwrap(); + + // Extract to new location + let extract_dir = tempfile::tempdir().unwrap(); + let reader = read_archive(&zip_file).await; + extract_zip(extract_dir.path(), reader).await.unwrap(); + + // Check permissions are preserved + let extracted_path = extract_dir.path().join("my_script"); + assert!(extracted_path.exists()); + let extracted_perms = std::fs::metadata(&extracted_path).unwrap().permissions(); + assert_eq!(extracted_perms.mode() & 0o777, 0o755); + }); + } +} diff --git a/third_party/gpui_support/gpui_util/src/command.rs b/third_party/gpui_support/gpui_util/src/command.rs new file mode 100644 index 0000000..85e2234 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/command.rs @@ -0,0 +1,32 @@ +use std::ffi::OsStr; + +#[cfg(target_os = "windows")] +const CREATE_NO_WINDOW: u32 = 0x0800_0000_u32; + +#[cfg(target_os = "windows")] +pub fn new_std_command(program: impl AsRef) -> std::process::Command { + use std::os::windows::process::CommandExt; + + let mut command = std::process::Command::new(program); + command.creation_flags(CREATE_NO_WINDOW); + command +} + +#[cfg(not(target_os = "windows"))] +pub fn new_std_command(program: impl AsRef) -> std::process::Command { + std::process::Command::new(program) +} + +#[cfg(target_os = "windows")] +pub fn new_smol_command(program: impl AsRef) -> smol::process::Command { + use smol::process::windows::CommandExt; + + let mut command = smol::process::Command::new(program); + command.creation_flags(CREATE_NO_WINDOW); + command +} + +#[cfg(not(target_os = "windows"))] +pub fn new_smol_command(program: impl AsRef) -> smol::process::Command { + smol::process::Command::new(program) +} diff --git a/third_party/gpui_support/gpui_util/src/fs.rs b/third_party/gpui_support/gpui_util/src/fs.rs new file mode 100644 index 0000000..60aab4a --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/fs.rs @@ -0,0 +1,111 @@ +use crate::ResultExt; +use anyhow::{Result, bail}; +use async_fs as fs; +use futures_lite::StreamExt; +use std::path::{Path, PathBuf}; + +/// Removes all files and directories matching the given predicate +pub async fn remove_matching(dir: &Path, predicate: F) +where + F: Fn(&Path) -> bool, +{ + if let Some(mut entries) = fs::read_dir(dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if predicate(entry_path.as_path()) + && let Ok(metadata) = fs::metadata(&entry_path).await + { + if metadata.is_file() { + fs::remove_file(&entry_path).await.log_err(); + } else { + fs::remove_dir_all(&entry_path).await.log_err(); + } + } + } + } + } +} + +pub async fn collect_matching(dir: &Path, predicate: F) -> Vec +where + F: Fn(&Path) -> bool, +{ + let mut matching = vec![]; + + if let Some(mut entries) = fs::read_dir(dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() + && predicate(entry.path().as_path()) + { + matching.push(entry.path()); + } + } + } + + matching +} + +pub async fn find_file_name_in_dir(dir: &Path, predicate: F) -> Option +where + F: Fn(&str) -> bool, +{ + if let Some(mut entries) = fs::read_dir(dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + + if let Some(file_name) = entry_path + .file_name() + .map(|file_name| file_name.to_string_lossy()) + && predicate(&file_name) + { + return Some(entry_path); + } + } + } + } + + None +} + +pub async fn move_folder_files_to_folder>( + source_path: P, + target_path: P, +) -> Result<()> { + if !target_path.as_ref().is_dir() { + bail!("Folder not found or is not a directory"); + } + + let mut entries = fs::read_dir(source_path.as_ref()).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + let old_path = entry.path(); + let new_path = target_path.as_ref().join(entry.file_name()); + + fs::rename(&old_path, &new_path).await?; + } + + fs::remove_dir(source_path).await?; + + Ok(()) +} + +#[cfg(unix)] +/// Set the permissions for the given path so that the file becomes executable. +/// This is a noop for non-unix platforms. +pub async fn make_file_executable(path: &Path) -> std::io::Result<()> { + fs::set_permissions( + path, + ::from_mode(0o755), + ) + .await +} + +#[cfg(not(unix))] +#[allow(clippy::unused_async)] +/// Set the permissions for the given path so that the file becomes executable. +/// This is a noop for non-unix platforms. +pub async fn make_file_executable(_path: &Path) -> std::io::Result<()> { + Ok(()) +} diff --git a/third_party/gpui_support/gpui_util/src/markdown.rs b/third_party/gpui_support/gpui_util/src/markdown.rs new file mode 100644 index 0000000..303dbe0 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/markdown.rs @@ -0,0 +1,268 @@ +use std::fmt::{Display, Formatter}; + +/// Indicates that the wrapped `String` is markdown text. +#[derive(Debug, Clone)] +pub struct MarkdownString(pub String); + +impl Display for MarkdownString { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Escapes markdown special characters in markdown text blocks. Markdown code blocks follow +/// different rules and `MarkdownInlineCode` or `MarkdownCodeBlock` should be used in that case. +/// +/// Also escapes the following markdown extensions: +/// +/// * `^` for superscripts +/// * `$` for inline math +/// * `~` for strikethrough +/// +/// Escape of some characters is unnecessary, because while they are involved in markdown syntax, +/// the other characters involved are escaped: +/// +/// * `!`, `]`, `(`, and `)` are used in link syntax, but `[` is escaped so these are parsed as +/// plaintext. +/// +/// * `;` is used in HTML entity syntax, but `&` is escaped, so they are parsed as plaintext. +/// +/// TODO: There is one escape this doesn't do currently. Period after numbers at the start of the +/// line (`[0-9]*\.`) should also be escaped to avoid it being interpreted as a list item. +pub struct MarkdownEscaped<'a>(pub &'a str); + +/// Implements `Display` to format markdown inline code (wrapped in backticks), handling code that +/// contains backticks and spaces. All whitespace is treated as a single space character. For text +/// that does not contain whitespace other than ' ', this escaping roundtrips through +/// pulldown-cmark. +/// +/// When used in tables, `|` should be escaped like `\|` in the text provided to this function. +pub struct MarkdownInlineCode<'a>(pub &'a str); + +/// Implements `Display` to format markdown code blocks, wrapped in 3 or more backticks as needed. +pub struct MarkdownCodeBlock<'a> { + pub tag: &'a str, + pub text: &'a str, +} + +impl Display for MarkdownEscaped<'_> { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + let mut start_of_unescaped = None; + for (ix, c) in self.0.char_indices() { + match c { + // Always escaped. + '\\' | '`' | '*' | '_' | '[' | '^' | '$' | '~' | '&' | + // TODO: these only need to be escaped when they are the first non-whitespace + // character of the line of a block. There should probably be both an `escape_block` + // which does this and an `escape_inline` method which does not escape these. + '#' | '+' | '=' | '-' => { + match start_of_unescaped { + None => {} + Some(start_of_unescaped) => { + write!(formatter, "{}", &self.0[start_of_unescaped..ix])?; + } + } + write!(formatter, "\\")?; + // Can include this char in the "unescaped" text since a + // backslash was just emitted. + start_of_unescaped = Some(ix); + } + // Escaped since `<` is used in opening HTML tags. `<` is used since Markdown + // supports HTML entities, and this allows the text to be used directly in HTML. + '<' => { + match start_of_unescaped { + None => {} + Some(start_of_unescaped) => { + write!(formatter, "{}", &self.0[start_of_unescaped..ix])?; + } + } + write!(formatter, "<")?; + start_of_unescaped = None; + } + // Escaped since `>` is used for blockquotes. `>` is used since Markdown supports + // HTML entities, and this allows the text to be used directly in HTML. + '>' => { + match start_of_unescaped { + None => {} + Some(start_of_unescaped) => { + write!(formatter, "{}", &self.0[start_of_unescaped..ix])?; + } + } + write!(formatter, ">")?; + start_of_unescaped = None; + } + _ => { + if start_of_unescaped.is_none() { + start_of_unescaped = Some(ix); + } + } + } + } + if let Some(start_of_unescaped) = start_of_unescaped { + write!(formatter, "{}", &self.0[start_of_unescaped..])?; + } + Ok(()) + } +} + +impl Display for MarkdownInlineCode<'_> { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + // Apache License 2.0, same as this crate. + // + // Copied from `pulldown-cmark-to-cmark-20.0.0` with modifications: + // + // * Handling of all whitespace. pulldown-cmark-to-cmark is anticipating + // `Code` events parsed by pulldown-cmark. + // + // https://github.com/Byron/pulldown-cmark-to-cmark/blob/3c850de2d3d1d79f19ca5f375e1089a653cf3ff7/src/lib.rs#L290 + + let mut all_whitespace = true; + let text = self + .0 + .chars() + .map(|c| { + if c.is_whitespace() { + ' ' + } else { + all_whitespace = false; + c + } + }) + .collect::(); + + // When inline code has leading and trailing ' ' characters, additional space is needed + // to escape it, unless all characters are space. + if all_whitespace { + write!(formatter, "`{text}`") + } else { + // More backticks are needed to delimit the inline code than the maximum number of + // backticks in a consecutive run. + let backticks = "`".repeat(count_max_consecutive_chars(&text, '`') + 1); + let space = match text.as_bytes() { + &[b'`', ..] | &[.., b'`'] => " ", // Space needed to separate backtick. + &[b' ', .., b' '] => " ", // Space needed to escape inner space. + _ => "", // No space needed. + }; + write!(formatter, "{backticks}{space}{text}{space}{backticks}") + } + } +} + +impl Display for MarkdownCodeBlock<'_> { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + let tag = self.tag; + let text = self.text; + let backticks = "`".repeat(3.max(count_max_consecutive_chars(text, '`') + 1)); + write!(formatter, "{backticks}{tag}\n{text}\n{backticks}\n") + } +} + +// Copied from `pulldown-cmark-to-cmark-20.0.0` with changed names. +// https://github.com/Byron/pulldown-cmark-to-cmark/blob/3c850de2d3d1d79f19ca5f375e1089a653cf3ff7/src/lib.rs#L1063 +// Apache License 2.0, same as this code. +fn count_max_consecutive_chars(text: &str, search: char) -> usize { + let mut in_search_chars = false; + let mut max_count = 0; + let mut cur_count = 0; + + for ch in text.chars() { + if ch == search { + cur_count += 1; + in_search_chars = true; + } else if in_search_chars { + max_count = max_count.max(cur_count); + cur_count = 0; + in_search_chars = false; + } + } + max_count.max(cur_count) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_markdown_escaped() { + let input = r#" + # Heading + + Another heading + === + + Another heading variant + --- + + Paragraph with [link](https://example.com) and `code`, *emphasis*, and ~strikethrough~. + + ``` + code block + ``` + + List with varying leaders: + - Item 1 + * Item 2 + + Item 3 + + Some math: $`\sqrt{3x-1}+(1+x)^2`$ + + HTML entity:   + "#; + + let expected = r#" + \# Heading + + Another heading + \=\=\= + + Another heading variant + \-\-\- + + Paragraph with \[link](https://example.com) and \`code\`, \*emphasis\*, and \~strikethrough\~. + + \`\`\` + code block + \`\`\` + + List with varying leaders: + \- Item 1 + \* Item 2 + \+ Item 3 + + Some math: \$\`\\sqrt{3x\-1}\+(1\+x)\^2\`\$ + + HTML entity: \  + "#; + + assert_eq!(MarkdownEscaped(input).to_string(), expected); + } + + #[test] + fn test_markdown_inline_code() { + assert_eq!(MarkdownInlineCode(" ").to_string(), "` `"); + assert_eq!(MarkdownInlineCode("text").to_string(), "`text`"); + assert_eq!(MarkdownInlineCode("text ").to_string(), "`text `"); + assert_eq!(MarkdownInlineCode(" text ").to_string(), "` text `"); + assert_eq!(MarkdownInlineCode("`").to_string(), "`` ` ``"); + assert_eq!(MarkdownInlineCode("``").to_string(), "``` `` ```"); + assert_eq!(MarkdownInlineCode("`text`").to_string(), "`` `text` ``"); + assert_eq!( + MarkdownInlineCode("some `text` no leading or trailing backticks").to_string(), + "``some `text` no leading or trailing backticks``" + ); + } + + #[test] + fn test_count_max_consecutive_chars() { + assert_eq!( + count_max_consecutive_chars("``a```b``", '`'), + 3, + "the highest seen consecutive segment of backticks counts" + ); + assert_eq!( + count_max_consecutive_chars("```a``b`", '`'), + 3, + "it can't be downgraded later" + ); + } +} diff --git a/third_party/gpui_support/gpui_util/src/paths.rs b/third_party/gpui_support/gpui_util/src/paths.rs new file mode 100644 index 0000000..59efaff --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/paths.rs @@ -0,0 +1,1934 @@ +use anyhow::Context; +use globset::{Glob, GlobSet, GlobSetBuilder}; +use itertools::Itertools; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::error::Error; +use std::fmt::{Display, Formatter}; +use std::mem; +use std::path::StripPrefixError; +use std::sync::{Arc, OnceLock}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + sync::LazyLock, +}; + +use crate::rel_path::RelPath; + +static HOME_DIR: OnceLock = OnceLock::new(); + +/// Returns the path to the user's home directory. +pub fn home_dir() -> &'static PathBuf { + HOME_DIR.get_or_init(|| { + if cfg!(any(test, feature = "test-support")) { + if cfg!(target_os = "macos") { + PathBuf::from("/Users/zed") + } else if cfg!(target_os = "windows") { + PathBuf::from("C:\\Users\\zed") + } else { + PathBuf::from("/home/zed") + } + } else { + dirs::home_dir().expect("failed to determine home directory") + } + }) +} + +pub trait PathExt { + /// Compacts a given file path by replacing the user's home directory + /// prefix with a tilde (`~`). + /// + /// # Returns + /// + /// * A `PathBuf` containing the compacted file path. If the input path + /// does not have the user's home directory prefix, or if we are not on + /// Linux or macOS, the original path is returned unchanged. + fn compact(&self) -> PathBuf; + + /// Returns a file's extension or, if the file is hidden, its name without the leading dot + fn extension_or_hidden_file_name(&self) -> Option<&str>; + + fn try_from_bytes<'a>(bytes: &'a [u8]) -> anyhow::Result + where + Self: From<&'a Path>, + { + #[cfg(unix)] + { + use std::os::unix::prelude::OsStrExt; + Ok(Self::from(Path::new(OsStr::from_bytes(bytes)))) + } + #[cfg(windows)] + { + use tendril::fmt::{Format, WTF8}; + WTF8::validate(bytes) + .then(|| { + // Safety: bytes are valid WTF-8 sequence. + Self::from(Path::new(unsafe { + OsStr::from_encoded_bytes_unchecked(bytes) + })) + }) + .with_context(|| format!("Invalid WTF-8 sequence: {bytes:?}")) + } + } + + /// Converts a local path to one that can be used inside of WSL. + /// Returns `None` if the path cannot be converted into a WSL one (network share). + fn local_to_wsl(&self) -> Option; + + /// Returns a file's "full" joined collection of extensions, in the case where a file does not + /// just have a singular extension but instead has multiple (e.g File.tar.gz, Component.stories.tsx) + /// + /// Will provide back the extensions joined together such as tar.gz or stories.tsx + fn multiple_extensions(&self) -> Option; + + /// Try to make a shell-safe representation of the path. + /// + /// For Unix, the path is escaped to be safe for POSIX shells + fn try_shell_safe(&self) -> anyhow::Result; +} + +impl> PathExt for T { + fn compact(&self) -> PathBuf { + if cfg!(any(target_os = "linux", target_os = "freebsd")) || cfg!(target_os = "macos") { + match self.as_ref().strip_prefix(home_dir().as_path()) { + Ok(relative_path) => { + let mut shortened_path = PathBuf::new(); + shortened_path.push("~"); + shortened_path.push(relative_path); + shortened_path + } + Err(_) => self.as_ref().to_path_buf(), + } + } else { + self.as_ref().to_path_buf() + } + } + + fn extension_or_hidden_file_name(&self) -> Option<&str> { + let path = self.as_ref(); + let file_name = path.file_name()?.to_str()?; + if file_name.starts_with('.') { + return file_name.strip_prefix('.'); + } + + path.extension() + .and_then(|e| e.to_str()) + .or_else(|| path.file_stem()?.to_str()) + } + + fn local_to_wsl(&self) -> Option { + // quite sketchy to convert this back to path at the end, but a lot of functions only accept paths + // todo: ideally rework them..? + let mut new_path = std::ffi::OsString::new(); + for component in self.as_ref().components() { + match component { + std::path::Component::Prefix(prefix) => { + let drive_letter = prefix.as_os_str().to_string_lossy().to_lowercase(); + let drive_letter = drive_letter.strip_suffix(':')?; + + new_path.push(format!("/mnt/{}", drive_letter)); + } + std::path::Component::RootDir => {} + std::path::Component::CurDir => { + new_path.push("/."); + } + std::path::Component::ParentDir => { + new_path.push("/.."); + } + std::path::Component::Normal(os_str) => { + new_path.push("/"); + new_path.push(os_str); + } + } + } + + Some(new_path.into()) + } + + fn multiple_extensions(&self) -> Option { + let path = self.as_ref(); + let file_name = path.file_name()?.to_str()?; + + let parts: Vec<&str> = file_name + .split('.') + // Skip the part with the file name extension + .skip(1) + .collect(); + + if parts.len() < 2 { + return None; + } + + Some(parts.into_iter().join(".")) + } + + fn try_shell_safe(&self) -> anyhow::Result { + #[cfg(target_os = "windows")] + { + Ok(self.as_ref().to_string_lossy().to_string()) + } + + #[cfg(not(target_os = "windows"))] + { + let path_str = self + .as_ref() + .to_str() + .with_context(|| "Path contains invalid UTF-8")?; + + // As of writing, this can only be fail if the path contains a null byte, which shouldn't be possible + // but shlex has annotated the error as #[non_exhaustive] so we can't make it a compile error if other + // errors are introduced in the future :( + Ok(shlex::try_quote(path_str)?.into_owned()) + } + } +} + +pub fn path_ends_with(base: &Path, suffix: &Path) -> bool { + strip_path_suffix(base, suffix).is_some() +} + +pub fn strip_path_suffix<'a>(base: &'a Path, suffix: &Path) -> Option<&'a Path> { + if let Some(remainder) = base + .as_os_str() + .as_encoded_bytes() + .strip_suffix(suffix.as_os_str().as_encoded_bytes()) + { + if remainder + .last() + .is_none_or(|last_byte| std::path::is_separator(*last_byte as char)) + { + let os_str = unsafe { + OsStr::from_encoded_bytes_unchecked( + &remainder[0..remainder.len().saturating_sub(1)], + ) + }; + return Some(Path::new(os_str)); + } + } + None +} + +/// In memory, this is identical to `Path`. On non-Windows conversions to this type are no-ops. On +/// windows, these conversions sanitize UNC paths by removing the `\\\\?\\` prefix. +#[derive(Eq, PartialEq, Hash, Ord, PartialOrd)] +#[repr(transparent)] +pub struct SanitizedPath(Path); + +impl SanitizedPath { + pub fn new + ?Sized>(path: &T) -> &Self { + #[cfg(not(target_os = "windows"))] + return Self::unchecked_new(path.as_ref()); + + #[cfg(target_os = "windows")] + return Self::unchecked_new(dunce::simplified(path.as_ref())); + } + + pub fn unchecked_new + ?Sized>(path: &T) -> &Self { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute::<&Path, &Self>(path.as_ref()) } + } + + pub fn from_arc(path: Arc) -> Arc { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + #[cfg(not(target_os = "windows"))] + return unsafe { mem::transmute::, Arc>(path) }; + + // TODO: could avoid allocating here if dunce::simplified results in the same path + #[cfg(target_os = "windows")] + return Self::new(&path).into(); + } + + pub fn new_arc + ?Sized>(path: &T) -> Arc { + Self::new(path).into() + } + + pub fn cast_arc(path: Arc) -> Arc { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute::, Arc>(path) } + } + + pub fn cast_arc_ref(path: &Arc) -> &Arc { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute::<&Arc, &Arc>(path) } + } + + pub fn starts_with(&self, prefix: &Self) -> bool { + self.0.starts_with(&prefix.0) + } + + pub fn as_path(&self) -> &Path { + &self.0 + } + + pub fn file_name(&self) -> Option<&std::ffi::OsStr> { + self.0.file_name() + } + + pub fn extension(&self) -> Option<&std::ffi::OsStr> { + self.0.extension() + } + + pub fn join>(&self, path: P) -> PathBuf { + self.0.join(path) + } + + pub fn parent(&self) -> Option<&Self> { + self.0.parent().map(Self::unchecked_new) + } + + pub fn strip_prefix(&self, base: &Self) -> Result<&Path, StripPrefixError> { + self.0.strip_prefix(base.as_path()) + } + + pub fn to_str(&self) -> Option<&str> { + self.0.to_str() + } + + pub fn to_path_buf(&self) -> PathBuf { + self.0.to_path_buf() + } +} + +impl std::fmt::Debug for SanitizedPath { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, formatter) + } +} + +impl Display for SanitizedPath { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.display()) + } +} + +impl From<&SanitizedPath> for Arc { + fn from(sanitized_path: &SanitizedPath) -> Self { + let path: Arc = sanitized_path.0.into(); + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute(path) } + } +} + +impl From<&SanitizedPath> for PathBuf { + fn from(sanitized_path: &SanitizedPath) -> Self { + sanitized_path.as_path().into() + } +} + +impl AsRef for SanitizedPath { + fn as_ref(&self) -> &Path { + &self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PathStyle { + Posix, + Windows, +} + +impl PathStyle { + #[cfg(target_os = "windows")] + pub const fn local() -> Self { + PathStyle::Windows + } + + #[cfg(not(target_os = "windows"))] + pub const fn local() -> Self { + PathStyle::Posix + } + + #[inline] + pub fn separator(&self) -> &'static str { + match self { + PathStyle::Posix => "/", + PathStyle::Windows => "\\", + } + } + + pub fn is_windows(&self) -> bool { + *self == PathStyle::Windows + } + + pub fn join(self, left: impl AsRef, right: impl AsRef) -> Option { + let right = right.as_ref().to_str()?; + if is_absolute(right, self) { + return None; + } + let left = left.as_ref().to_str()?; + if left.is_empty() { + Some(right.into()) + } else { + Some(format!( + "{left}{}{right}", + if left.ends_with(self.separator()) { + "" + } else { + self.separator() + } + )) + } + } + + pub fn split(self, path_like: &str) -> (Option<&str>, &str) { + let Some(pos) = path_like.rfind(self.separator()) else { + return (None, path_like); + }; + let filename_start = pos + self.separator().len(); + ( + Some(&path_like[..filename_start]), + &path_like[filename_start..], + ) + } +} + +#[derive(Debug, Clone)] +pub struct RemotePathBuf { + style: PathStyle, + string: String, +} + +impl RemotePathBuf { + pub fn new(string: String, style: PathStyle) -> Self { + Self { style, string } + } + + pub fn from_str(path: &str, style: PathStyle) -> Self { + Self::new(path.to_string(), style) + } + + pub fn path_style(&self) -> PathStyle { + self.style + } + + pub fn to_proto(self) -> String { + self.string + } +} + +impl Display for RemotePathBuf { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.string) + } +} + +pub fn is_absolute(path_like: &str, path_style: PathStyle) -> bool { + path_like.starts_with('/') + || path_style == PathStyle::Windows + && (path_like.starts_with('\\') + || path_like + .chars() + .next() + .is_some_and(|c| c.is_ascii_alphabetic()) + && path_like[1..] + .strip_prefix(':') + .is_some_and(|path| path.starts_with('/') || path.starts_with('\\'))) +} + +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct NormalizeError; + +impl Error for NormalizeError {} + +impl std::fmt::Display for NormalizeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("parent reference `..` points outside of base directory") + } +} + +/// Copied from stdlib where it's unstable. +/// +/// Normalize a path, including `..` without traversing the filesystem. +/// +/// Returns an error if normalization would leave leading `..` components. +/// +///
+/// +/// This function always resolves `..` to the "lexical" parent. +/// That is "a/b/../c" will always resolve to `a/c` which can change the meaning of the path. +/// In particular, `a/c` and `a/b/../c` are distinct on many systems because `b` may be a symbolic link, so its parent isn't `a`. +/// +///
+/// +/// [`path::absolute`](absolute) is an alternative that preserves `..`. +/// Or [`Path::canonicalize`] can be used to resolve any `..` by querying the filesystem. +pub fn normalize_lexically(path: &Path) -> Result { + use std::path::Component; + + let mut lexical = PathBuf::new(); + let mut iter = path.components().peekable(); + + // Find the root, if any, and add it to the lexical path. + // Here we treat the Windows path "C:\" as a single "root" even though + // `components` splits it into two: (Prefix, RootDir). + let root = match iter.peek() { + Some(Component::ParentDir) => return Err(NormalizeError), + Some(p @ Component::RootDir) | Some(p @ Component::CurDir) => { + lexical.push(p); + iter.next(); + lexical.as_os_str().len() + } + Some(Component::Prefix(prefix)) => { + lexical.push(prefix.as_os_str()); + iter.next(); + if let Some(p @ Component::RootDir) = iter.peek() { + lexical.push(p); + iter.next(); + } + lexical.as_os_str().len() + } + None => return Ok(PathBuf::new()), + Some(Component::Normal(_)) => 0, + }; + + for component in iter { + match component { + Component::RootDir => unreachable!(), + Component::Prefix(_) => return Err(NormalizeError), + Component::CurDir => continue, + Component::ParentDir => { + // It's an error if ParentDir causes us to go above the "root". + if lexical.as_os_str().len() == root { + return Err(NormalizeError); + } else { + lexical.pop(); + } + } + Component::Normal(path) => lexical.push(path), + } + } + Ok(lexical) +} + +/// A delimiter to use in `path_query:row_number:column_number` strings parsing. +pub const FILE_ROW_COLUMN_DELIMITER: char = ':'; + +const ROW_COL_CAPTURE_REGEX: &str = r"(?xs) + ([^\(]+)\:(?: + \((\d+)[,:](\d+)\) # filename:(row,column), filename:(row:column) + | + \((\d+)\)() # filename:(row) + ) + | + ([^\(]+)(?: + \((\d+)[,:](\d+)\) # filename(row,column), filename(row:column) + | + \((\d+)\)() # filename(row) + ) + | + (.+?)(?: + \:+(\d+)\:(\d+)\:*$ # filename:row:column + | + \:+(\d+)\:*()$ # filename:row + | + \:+()()$ + )"; + +/// A representation of a path-like string with optional row and column numbers. +/// Matching values example: `te`, `test.rs:22`, `te:22:5`, `test.c(22)`, `test.c(22,5)`etc. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +pub struct PathWithPosition { + pub path: PathBuf, + pub row: Option, + // Absent if row is absent. + pub column: Option, +} + +impl PathWithPosition { + /// Returns a PathWithPosition from a path. + pub fn from_path(path: PathBuf) -> Self { + Self { + path, + row: None, + column: None, + } + } + + /// Parses a string that possibly has `:row:column` or `(row, column)` suffix. + /// Parenthesis format is used by [MSBuild](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-diagnostic-format-for-tasks) compatible tools + /// Ignores trailing `:`s, so `test.rs:22:` is parsed as `test.rs:22`. + /// If the suffix parsing fails, the whole string is parsed as a path. + /// + /// Be mindful that `test_file:10:1:` is a valid posix filename. + /// `PathWithPosition` class assumes that the ending position-like suffix is **not** part of the filename. + /// + /// # Examples + /// + /// ``` + /// # use util::paths::PathWithPosition; + /// # use std::path::PathBuf; + /// assert_eq!(PathWithPosition::parse_str("test_file"), PathWithPosition { + /// path: PathBuf::from("test_file"), + /// row: None, + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file:10"), PathWithPosition { + /// path: PathBuf::from("test_file"), + /// row: Some(10), + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: None, + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:1"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: Some(1), + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:1:2"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: Some(1), + /// column: Some(2), + /// }); + /// ``` + /// + /// # Expected parsing results when encounter ill-formatted inputs. + /// ``` + /// # use util::paths::PathWithPosition; + /// # use std::path::PathBuf; + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:a"), PathWithPosition { + /// path: PathBuf::from("test_file.rs:a"), + /// row: None, + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:a:b"), PathWithPosition { + /// path: PathBuf::from("test_file.rs:a:b"), + /// row: None, + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: None, + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs::1"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: Some(1), + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:1::"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: Some(1), + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs::1:2"), PathWithPosition { + /// path: PathBuf::from("test_file.rs"), + /// row: Some(1), + /// column: Some(2), + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:1::2"), PathWithPosition { + /// path: PathBuf::from("test_file.rs:1"), + /// row: Some(2), + /// column: None, + /// }); + /// assert_eq!(PathWithPosition::parse_str("test_file.rs:1:2:3"), PathWithPosition { + /// path: PathBuf::from("test_file.rs:1"), + /// row: Some(2), + /// column: Some(3), + /// }); + /// ``` + pub fn parse_str(s: &str) -> Self { + let trimmed = s.trim(); + let path = Path::new(trimmed); + let maybe_file_name_with_row_col = path.file_name().unwrap_or_default().to_string_lossy(); + if maybe_file_name_with_row_col.is_empty() { + return Self { + path: Path::new(s).to_path_buf(), + row: None, + column: None, + }; + } + + // Let's avoid repeated init cost on this. It is subject to thread contention, but + // so far this code isn't called from multiple hot paths. Getting contention here + // in the future seems unlikely. + static SUFFIX_RE: LazyLock = + LazyLock::new(|| Regex::new(ROW_COL_CAPTURE_REGEX).unwrap()); + match SUFFIX_RE + .captures(&maybe_file_name_with_row_col) + .map(|caps| caps.extract()) + { + Some((_, [file_name, maybe_row, maybe_column])) => { + let row = maybe_row.parse::().ok(); + let column = maybe_column.parse::().ok(); + + let suffix_length = maybe_file_name_with_row_col.len() - file_name.len(); + let path_without_suffix = &trimmed[..trimmed.len() - suffix_length]; + + Self { + path: Path::new(path_without_suffix).to_path_buf(), + row, + column, + } + } + None => { + // The `ROW_COL_CAPTURE_REGEX` deals with separated digits only, + // but in reality there could be `foo/bar.py:22:in` inputs which we want to match too. + // The regex mentioned is not very extendable with "digit or random string" checks, so do this here instead. + let delimiter = ':'; + let mut path_parts = s + .rsplitn(3, delimiter) + .collect::>() + .into_iter() + .rev() + .fuse(); + let mut path_string = path_parts.next().expect("rsplitn should have the rest of the string as its last parameter that we reversed").to_owned(); + let mut row = None; + let mut column = None; + if let Some(maybe_row) = path_parts.next() { + if let Ok(parsed_row) = maybe_row.parse::() { + row = Some(parsed_row); + if let Some(parsed_column) = path_parts + .next() + .and_then(|maybe_col| maybe_col.parse::().ok()) + { + column = Some(parsed_column); + } + } else { + path_string.push(delimiter); + path_string.push_str(maybe_row); + } + } + for split in path_parts { + path_string.push(delimiter); + path_string.push_str(split); + } + + Self { + path: PathBuf::from(path_string), + row, + column, + } + } + } + } + + pub fn map_path( + self, + mapping: impl FnOnce(PathBuf) -> Result, + ) -> Result { + Ok(PathWithPosition { + path: mapping(self.path)?, + row: self.row, + column: self.column, + }) + } + + pub fn to_string(&self, path_to_string: impl Fn(&PathBuf) -> String) -> String { + let path_string = path_to_string(&self.path); + if let Some(row) = self.row { + if let Some(column) = self.column { + format!("{path_string}:{row}:{column}") + } else { + format!("{path_string}:{row}") + } + } else { + path_string + } + } +} + +#[derive(Clone, Debug)] +pub struct PathMatcher { + sources: Vec, + glob: GlobSet, + path_style: PathStyle, +} + +// impl std::fmt::Display for PathMatcher { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// self.sources.fmt(f) +// } +// } + +impl PartialEq for PathMatcher { + fn eq(&self, other: &Self) -> bool { + self.sources.eq(&other.sources) + } +} + +impl Eq for PathMatcher {} + +impl PathMatcher { + pub fn new( + globs: impl IntoIterator>, + path_style: PathStyle, + ) -> Result { + let globs = globs + .into_iter() + .map(|as_str| Glob::new(as_str.as_ref())) + .collect::, _>>()?; + let sources = globs.iter().map(|glob| glob.glob().to_owned()).collect(); + let mut glob_builder = GlobSetBuilder::new(); + for single_glob in globs { + glob_builder.add(single_glob); + } + let glob = glob_builder.build()?; + Ok(PathMatcher { + glob, + sources, + path_style, + }) + } + + pub fn sources(&self) -> &[String] { + &self.sources + } + + pub fn is_match>(&self, other: P) -> bool { + let other_path = other.as_ref(); + self.sources.iter().any(|source| { + let as_bytes = other_path.as_os_str().as_encoded_bytes(); + as_bytes.starts_with(source.as_bytes()) || as_bytes.ends_with(source.as_bytes()) + }) || self.glob.is_match(other_path) + || self.check_with_end_separator(other_path) + } + + fn check_with_end_separator(&self, path: &Path) -> bool { + let path_str = path.to_string_lossy(); + let separator = self.path_style.separator(); + if path_str.ends_with(separator) { + false + } else { + self.glob.is_match(path_str.to_string() + separator) + } + } +} + +impl Default for PathMatcher { + fn default() -> Self { + Self { + path_style: PathStyle::local(), + glob: GlobSet::empty(), + sources: vec![], + } + } +} + +/// Custom character comparison that prioritizes lowercase for same letters +fn compare_chars(a: char, b: char) -> Ordering { + // First compare case-insensitive + match a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()) { + Ordering::Equal => { + // If same letter, prioritize lowercase (lowercase < uppercase) + match (a.is_ascii_lowercase(), b.is_ascii_lowercase()) { + (true, false) => Ordering::Less, // lowercase comes first + (false, true) => Ordering::Greater, // uppercase comes after + _ => Ordering::Equal, // both same case or both non-ascii + } + } + other => other, + } +} + +/// Compares two sequences of consecutive digits for natural sorting. +/// +/// This function is a core component of natural sorting that handles numeric comparison +/// in a way that feels natural to humans. It extracts and compares consecutive digit +/// sequences from two iterators, handling various cases like leading zeros and very large numbers. +/// +/// # Behavior +/// +/// The function implements the following comparison rules: +/// 1. Different numeric values: Compares by actual numeric value (e.g., "2" < "10") +/// 2. Leading zeros: When values are equal, longer sequence wins (e.g., "002" > "2") +/// 3. Large numbers: Falls back to string comparison for numbers that would overflow u128 +/// +/// # Examples +/// +/// ```text +/// "1" vs "2" -> Less (different values) +/// "2" vs "10" -> Less (numeric comparison) +/// "002" vs "2" -> Greater (leading zeros) +/// "10" vs "010" -> Less (leading zeros) +/// "999..." vs "1000..." -> Less (large number comparison) +/// ``` +/// +/// # Implementation Details +/// +/// 1. Extracts consecutive digits into strings +/// 2. Compares sequence lengths for leading zero handling +/// 3. For equal lengths, compares digit by digit +/// 4. For different lengths: +/// - Attempts numeric comparison first (for numbers up to 2^128 - 1) +/// - Falls back to string comparison if numbers would overflow +/// +/// The function advances both iterators past their respective numeric sequences, +/// regardless of the comparison result. +fn compare_numeric_segments( + a_iter: &mut std::iter::Peekable, + b_iter: &mut std::iter::Peekable, +) -> Ordering +where + I: Iterator, +{ + // Collect all consecutive digits into strings + let mut a_num_str = String::new(); + let mut b_num_str = String::new(); + + while let Some(&c) = a_iter.peek() { + if !c.is_ascii_digit() { + break; + } + + a_num_str.push(c); + a_iter.next(); + } + + while let Some(&c) = b_iter.peek() { + if !c.is_ascii_digit() { + break; + } + + b_num_str.push(c); + b_iter.next(); + } + + // First compare lengths (handle leading zeros) + match a_num_str.len().cmp(&b_num_str.len()) { + Ordering::Equal => { + // Same length, compare digit by digit + match a_num_str.cmp(&b_num_str) { + Ordering::Equal => Ordering::Equal, + ordering => ordering, + } + } + + // Different lengths but same value means leading zeros + ordering => { + // Try parsing as numbers first + if let (Ok(a_val), Ok(b_val)) = (a_num_str.parse::(), b_num_str.parse::()) { + match a_val.cmp(&b_val) { + Ordering::Equal => ordering, // Same value, longer one is greater (leading zeros) + ord => ord, + } + } else { + // If parsing fails (overflow), compare as strings + a_num_str.cmp(&b_num_str) + } + } + } +} + +/// Performs natural sorting comparison between two strings. +/// +/// Natural sorting is an ordering that handles numeric sequences in a way that matches human expectations. +/// For example, "file2" comes before "file10" (unlike standard lexicographic sorting). +/// +/// # Characteristics +/// +/// * Case-sensitive with lowercase priority: When comparing same letters, lowercase comes before uppercase +/// * Numbers are compared by numeric value, not character by character +/// * Leading zeros affect ordering when numeric values are equal +/// * Can handle numbers larger than u128::MAX (falls back to string comparison) +/// +/// # Algorithm +/// +/// The function works by: +/// 1. Processing strings character by character +/// 2. When encountering digits, treating consecutive digits as a single number +/// 3. Comparing numbers by their numeric value rather than lexicographically +/// 4. For non-numeric characters, using case-sensitive comparison with lowercase priority +fn natural_sort(a: &str, b: &str) -> Ordering { + let mut a_iter = a.chars().peekable(); + let mut b_iter = b.chars().peekable(); + + loop { + match (a_iter.peek(), b_iter.peek()) { + (None, None) => return Ordering::Equal, + (None, _) => return Ordering::Less, + (_, None) => return Ordering::Greater, + (Some(&a_char), Some(&b_char)) => { + if a_char.is_ascii_digit() && b_char.is_ascii_digit() { + match compare_numeric_segments(&mut a_iter, &mut b_iter) { + Ordering::Equal => continue, + ordering => return ordering, + } + } else { + match compare_chars(a_char, b_char) { + Ordering::Equal => { + a_iter.next(); + b_iter.next(); + } + ordering => return ordering, + } + } + } + } + } +} +pub fn compare_rel_paths( + (path_a, a_is_file): (&RelPath, bool), + (path_b, b_is_file): (&RelPath, bool), +) -> Ordering { + let mut components_a = path_a.components(); + let mut components_b = path_b.components(); + + fn stem_and_extension(filename: &str) -> (Option<&str>, Option<&str>) { + if filename.is_empty() { + return (None, None); + } + + match filename.rsplit_once('.') { + // Case 1: No dot was found. The entire name is the stem. + None => (Some(filename), None), + + // Case 2: A dot was found. + Some((before, after)) => { + // This is the crucial check for dotfiles like ".bashrc". + // If `before` is empty, the dot was the first character. + // In that case, we revert to the "whole name is the stem" logic. + if before.is_empty() { + (Some(filename), None) + } else { + // Otherwise, we have a standard stem and extension. + (Some(before), Some(after)) + } + } + } + } + loop { + match (components_a.next(), components_b.next()) { + (Some(component_a), Some(component_b)) => { + let a_is_file = a_is_file && components_a.rest().is_empty(); + let b_is_file = b_is_file && components_b.rest().is_empty(); + + let ordering = a_is_file.cmp(&b_is_file).then_with(|| { + let (a_stem, a_extension) = a_is_file + .then(|| stem_and_extension(component_a)) + .unwrap_or_default(); + let path_string_a = if a_is_file { a_stem } else { Some(component_a) }; + + let (b_stem, b_extension) = b_is_file + .then(|| stem_and_extension(component_b)) + .unwrap_or_default(); + let path_string_b = if b_is_file { b_stem } else { Some(component_b) }; + + let compare_components = match (path_string_a, path_string_b) { + (Some(a), Some(b)) => natural_sort(&a, &b), + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (None, None) => Ordering::Equal, + }; + + compare_components.then_with(|| { + if a_is_file && b_is_file { + let ext_a = a_extension.unwrap_or_default(); + let ext_b = b_extension.unwrap_or_default(); + ext_a.cmp(ext_b) + } else { + Ordering::Equal + } + }) + }); + + if !ordering.is_eq() { + return ordering; + } + } + (Some(_), None) => break Ordering::Greater, + (None, Some(_)) => break Ordering::Less, + (None, None) => break Ordering::Equal, + } + } +} + +pub fn compare_paths( + (path_a, a_is_file): (&Path, bool), + (path_b, b_is_file): (&Path, bool), +) -> Ordering { + let mut components_a = path_a.components().peekable(); + let mut components_b = path_b.components().peekable(); + + loop { + match (components_a.next(), components_b.next()) { + (Some(component_a), Some(component_b)) => { + let a_is_file = components_a.peek().is_none() && a_is_file; + let b_is_file = components_b.peek().is_none() && b_is_file; + + let ordering = a_is_file.cmp(&b_is_file).then_with(|| { + let path_a = Path::new(component_a.as_os_str()); + let path_string_a = if a_is_file { + path_a.file_stem() + } else { + path_a.file_name() + } + .map(|s| s.to_string_lossy()); + + let path_b = Path::new(component_b.as_os_str()); + let path_string_b = if b_is_file { + path_b.file_stem() + } else { + path_b.file_name() + } + .map(|s| s.to_string_lossy()); + + let compare_components = match (path_string_a, path_string_b) { + (Some(a), Some(b)) => natural_sort(&a, &b), + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (None, None) => Ordering::Equal, + }; + + compare_components.then_with(|| { + if a_is_file && b_is_file { + let ext_a = path_a.extension().unwrap_or_default(); + let ext_b = path_b.extension().unwrap_or_default(); + ext_a.cmp(ext_b) + } else { + Ordering::Equal + } + }) + }); + + if !ordering.is_eq() { + return ordering; + } + } + (Some(_), None) => break Ordering::Greater, + (None, Some(_)) => break Ordering::Less, + (None, None) => break Ordering::Equal, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use util_macros::perf; + + #[perf] + fn compare_paths_with_dots() { + let mut paths = vec![ + (Path::new("test_dirs"), false), + (Path::new("test_dirs/1.46"), false), + (Path::new("test_dirs/1.46/bar_1"), true), + (Path::new("test_dirs/1.46/bar_2"), true), + (Path::new("test_dirs/1.45"), false), + (Path::new("test_dirs/1.45/foo_2"), true), + (Path::new("test_dirs/1.45/foo_1"), true), + ]; + paths.sort_by(|&a, &b| compare_paths(a, b)); + assert_eq!( + paths, + vec![ + (Path::new("test_dirs"), false), + (Path::new("test_dirs/1.45"), false), + (Path::new("test_dirs/1.45/foo_1"), true), + (Path::new("test_dirs/1.45/foo_2"), true), + (Path::new("test_dirs/1.46"), false), + (Path::new("test_dirs/1.46/bar_1"), true), + (Path::new("test_dirs/1.46/bar_2"), true), + ] + ); + let mut paths = vec![ + (Path::new("root1/one.txt"), true), + (Path::new("root1/one.two.txt"), true), + ]; + paths.sort_by(|&a, &b| compare_paths(a, b)); + assert_eq!( + paths, + vec![ + (Path::new("root1/one.txt"), true), + (Path::new("root1/one.two.txt"), true), + ] + ); + } + + #[perf] + fn compare_paths_with_same_name_different_extensions() { + let mut paths = vec![ + (Path::new("test_dirs/file.rs"), true), + (Path::new("test_dirs/file.txt"), true), + (Path::new("test_dirs/file.md"), true), + (Path::new("test_dirs/file"), true), + (Path::new("test_dirs/file.a"), true), + ]; + paths.sort_by(|&a, &b| compare_paths(a, b)); + assert_eq!( + paths, + vec![ + (Path::new("test_dirs/file"), true), + (Path::new("test_dirs/file.a"), true), + (Path::new("test_dirs/file.md"), true), + (Path::new("test_dirs/file.rs"), true), + (Path::new("test_dirs/file.txt"), true), + ] + ); + } + + #[perf] + fn compare_paths_case_semi_sensitive() { + let mut paths = vec![ + (Path::new("test_DIRS"), false), + (Path::new("test_DIRS/foo_1"), true), + (Path::new("test_DIRS/foo_2"), true), + (Path::new("test_DIRS/bar"), true), + (Path::new("test_DIRS/BAR"), true), + (Path::new("test_dirs"), false), + (Path::new("test_dirs/foo_1"), true), + (Path::new("test_dirs/foo_2"), true), + (Path::new("test_dirs/bar"), true), + (Path::new("test_dirs/BAR"), true), + ]; + paths.sort_by(|&a, &b| compare_paths(a, b)); + assert_eq!( + paths, + vec![ + (Path::new("test_dirs"), false), + (Path::new("test_dirs/bar"), true), + (Path::new("test_dirs/BAR"), true), + (Path::new("test_dirs/foo_1"), true), + (Path::new("test_dirs/foo_2"), true), + (Path::new("test_DIRS"), false), + (Path::new("test_DIRS/bar"), true), + (Path::new("test_DIRS/BAR"), true), + (Path::new("test_DIRS/foo_1"), true), + (Path::new("test_DIRS/foo_2"), true), + ] + ); + } + + #[perf] + fn path_with_position_parse_posix_path() { + // Test POSIX filename edge cases + // Read more at https://en.wikipedia.org/wiki/Filename + assert_eq!( + PathWithPosition::parse_str("test_file"), + PathWithPosition { + path: PathBuf::from("test_file"), + row: None, + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("a:bc:.zip:1"), + PathWithPosition { + path: PathBuf::from("a:bc:.zip"), + row: Some(1), + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("one.second.zip:1"), + PathWithPosition { + path: PathBuf::from("one.second.zip"), + row: Some(1), + column: None + } + ); + + // Trim off trailing `:`s for otherwise valid input. + assert_eq!( + PathWithPosition::parse_str("test_file:10:1:"), + PathWithPosition { + path: PathBuf::from("test_file"), + row: Some(10), + column: Some(1) + } + ); + + assert_eq!( + PathWithPosition::parse_str("test_file.rs:"), + PathWithPosition { + path: PathBuf::from("test_file.rs"), + row: None, + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("test_file.rs:1:"), + PathWithPosition { + path: PathBuf::from("test_file.rs"), + row: Some(1), + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("ab\ncd"), + PathWithPosition { + path: PathBuf::from("ab\ncd"), + row: None, + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("👋\nab"), + PathWithPosition { + path: PathBuf::from("👋\nab"), + row: None, + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("Types.hs:(617,9)-(670,28):"), + PathWithPosition { + path: PathBuf::from("Types.hs"), + row: Some(617), + column: Some(9), + } + ); + } + + #[perf] + #[cfg(not(target_os = "windows"))] + fn path_with_position_parse_posix_path_with_suffix() { + assert_eq!( + PathWithPosition::parse_str("foo/bar:34:in"), + PathWithPosition { + path: PathBuf::from("foo/bar"), + row: Some(34), + column: None, + } + ); + assert_eq!( + PathWithPosition::parse_str("foo/bar.rs:1902:::15:"), + PathWithPosition { + path: PathBuf::from("foo/bar.rs:1902"), + row: Some(15), + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("app-editors:zed-0.143.6:20240710-201212.log:34:"), + PathWithPosition { + path: PathBuf::from("app-editors:zed-0.143.6:20240710-201212.log"), + row: Some(34), + column: None, + } + ); + + assert_eq!( + PathWithPosition::parse_str("crates/file_finder/src/file_finder.rs:1902:13:"), + PathWithPosition { + path: PathBuf::from("crates/file_finder/src/file_finder.rs"), + row: Some(1902), + column: Some(13), + } + ); + + assert_eq!( + PathWithPosition::parse_str("crate/utils/src/test:today.log:34"), + PathWithPosition { + path: PathBuf::from("crate/utils/src/test:today.log"), + row: Some(34), + column: None, + } + ); + assert_eq!( + PathWithPosition::parse_str("/testing/out/src/file_finder.odin(7:15)"), + PathWithPosition { + path: PathBuf::from("/testing/out/src/file_finder.odin"), + row: Some(7), + column: Some(15), + } + ); + } + + #[perf] + #[cfg(target_os = "windows")] + fn path_with_position_parse_windows_path() { + assert_eq!( + PathWithPosition::parse_str("crates\\utils\\paths.rs"), + PathWithPosition { + path: PathBuf::from("crates\\utils\\paths.rs"), + row: None, + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("C:\\Users\\someone\\test_file.rs"), + PathWithPosition { + path: PathBuf::from("C:\\Users\\someone\\test_file.rs"), + row: None, + column: None + } + ); + } + + #[perf] + #[cfg(target_os = "windows")] + fn path_with_position_parse_windows_path_with_suffix() { + assert_eq!( + PathWithPosition::parse_str("crates\\utils\\paths.rs:101"), + PathWithPosition { + path: PathBuf::from("crates\\utils\\paths.rs"), + row: Some(101), + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("\\\\?\\C:\\Users\\someone\\test_file.rs:1:20"), + PathWithPosition { + path: PathBuf::from("\\\\?\\C:\\Users\\someone\\test_file.rs"), + row: Some(1), + column: Some(20) + } + ); + + assert_eq!( + PathWithPosition::parse_str("C:\\Users\\someone\\test_file.rs(1902,13)"), + PathWithPosition { + path: PathBuf::from("C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: Some(13) + } + ); + + // Trim off trailing `:`s for otherwise valid input. + assert_eq!( + PathWithPosition::parse_str("\\\\?\\C:\\Users\\someone\\test_file.rs:1902:13:"), + PathWithPosition { + path: PathBuf::from("\\\\?\\C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: Some(13) + } + ); + + assert_eq!( + PathWithPosition::parse_str("\\\\?\\C:\\Users\\someone\\test_file.rs:1902:13:15:"), + PathWithPosition { + path: PathBuf::from("\\\\?\\C:\\Users\\someone\\test_file.rs:1902"), + row: Some(13), + column: Some(15) + } + ); + + assert_eq!( + PathWithPosition::parse_str("\\\\?\\C:\\Users\\someone\\test_file.rs:1902:::15:"), + PathWithPosition { + path: PathBuf::from("\\\\?\\C:\\Users\\someone\\test_file.rs:1902"), + row: Some(15), + column: None + } + ); + + assert_eq!( + PathWithPosition::parse_str("\\\\?\\C:\\Users\\someone\\test_file.rs(1902,13):"), + PathWithPosition { + path: PathBuf::from("\\\\?\\C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: Some(13), + } + ); + + assert_eq!( + PathWithPosition::parse_str("\\\\?\\C:\\Users\\someone\\test_file.rs(1902):"), + PathWithPosition { + path: PathBuf::from("\\\\?\\C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: None, + } + ); + + assert_eq!( + PathWithPosition::parse_str("C:\\Users\\someone\\test_file.rs:1902:13:"), + PathWithPosition { + path: PathBuf::from("C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: Some(13), + } + ); + + assert_eq!( + PathWithPosition::parse_str("C:\\Users\\someone\\test_file.rs(1902,13):"), + PathWithPosition { + path: PathBuf::from("C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: Some(13), + } + ); + + assert_eq!( + PathWithPosition::parse_str("C:\\Users\\someone\\test_file.rs(1902):"), + PathWithPosition { + path: PathBuf::from("C:\\Users\\someone\\test_file.rs"), + row: Some(1902), + column: None, + } + ); + + assert_eq!( + PathWithPosition::parse_str("crates/utils/paths.rs:101"), + PathWithPosition { + path: PathBuf::from("crates\\utils\\paths.rs"), + row: Some(101), + column: None, + } + ); + } + + #[perf] + fn test_path_compact() { + let path: PathBuf = [ + home_dir().to_string_lossy().into_owned(), + "some_file.txt".to_string(), + ] + .iter() + .collect(); + if cfg!(any(target_os = "linux", target_os = "freebsd")) || cfg!(target_os = "macos") { + assert_eq!(path.compact().to_str(), Some("~/some_file.txt")); + } else { + assert_eq!(path.compact().to_str(), path.to_str()); + } + } + + #[perf] + fn test_extension_or_hidden_file_name() { + // No dots in name + let path = Path::new("/a/b/c/file_name.rs"); + assert_eq!(path.extension_or_hidden_file_name(), Some("rs")); + + // Single dot in name + let path = Path::new("/a/b/c/file.name.rs"); + assert_eq!(path.extension_or_hidden_file_name(), Some("rs")); + + // Multiple dots in name + let path = Path::new("/a/b/c/long.file.name.rs"); + assert_eq!(path.extension_or_hidden_file_name(), Some("rs")); + + // Hidden file, no extension + let path = Path::new("/a/b/c/.gitignore"); + assert_eq!(path.extension_or_hidden_file_name(), Some("gitignore")); + + // Hidden file, with extension + let path = Path::new("/a/b/c/.eslintrc.js"); + assert_eq!(path.extension_or_hidden_file_name(), Some("eslintrc.js")); + } + + #[perf] + fn edge_of_glob() { + let path = Path::new("/work/node_modules"); + let path_matcher = + PathMatcher::new(&["**/node_modules/**".to_owned()], PathStyle::Posix).unwrap(); + assert!( + path_matcher.is_match(path), + "Path matcher should match {path:?}" + ); + } + + #[perf] + fn project_search() { + let path = Path::new("/Users/someonetoignore/work/zed/zed.dev/node_modules"); + let path_matcher = + PathMatcher::new(&["**/node_modules/**".to_owned()], PathStyle::Posix).unwrap(); + assert!( + path_matcher.is_match(path), + "Path matcher should match {path:?}" + ); + } + + #[perf] + #[cfg(target_os = "windows")] + fn test_sanitized_path() { + let path = Path::new("C:\\Users\\someone\\test_file.rs"); + let sanitized_path = SanitizedPath::new(path); + assert_eq!( + sanitized_path.to_string(), + "C:\\Users\\someone\\test_file.rs" + ); + + let path = Path::new("\\\\?\\C:\\Users\\someone\\test_file.rs"); + let sanitized_path = SanitizedPath::new(path); + assert_eq!( + sanitized_path.to_string(), + "C:\\Users\\someone\\test_file.rs" + ); + } + + #[perf] + fn test_compare_numeric_segments() { + // Helper function to create peekable iterators and test + fn compare(a: &str, b: &str) -> Ordering { + let mut a_iter = a.chars().peekable(); + let mut b_iter = b.chars().peekable(); + + let result = compare_numeric_segments(&mut a_iter, &mut b_iter); + + // Verify iterators advanced correctly + assert!( + !a_iter.next().is_some_and(|c| c.is_ascii_digit()), + "Iterator a should have consumed all digits" + ); + assert!( + !b_iter.next().is_some_and(|c| c.is_ascii_digit()), + "Iterator b should have consumed all digits" + ); + + result + } + + // Basic numeric comparisons + assert_eq!(compare("0", "0"), Ordering::Equal); + assert_eq!(compare("1", "2"), Ordering::Less); + assert_eq!(compare("9", "10"), Ordering::Less); + assert_eq!(compare("10", "9"), Ordering::Greater); + assert_eq!(compare("99", "100"), Ordering::Less); + + // Leading zeros + assert_eq!(compare("0", "00"), Ordering::Less); + assert_eq!(compare("00", "0"), Ordering::Greater); + assert_eq!(compare("01", "1"), Ordering::Greater); + assert_eq!(compare("001", "1"), Ordering::Greater); + assert_eq!(compare("001", "01"), Ordering::Greater); + + // Same value different representation + assert_eq!(compare("000100", "100"), Ordering::Greater); + assert_eq!(compare("100", "0100"), Ordering::Less); + assert_eq!(compare("0100", "00100"), Ordering::Less); + + // Large numbers + assert_eq!(compare("9999999999", "10000000000"), Ordering::Less); + assert_eq!( + compare( + "340282366920938463463374607431768211455", // u128::MAX + "340282366920938463463374607431768211456" + ), + Ordering::Less + ); + assert_eq!( + compare( + "340282366920938463463374607431768211456", // > u128::MAX + "340282366920938463463374607431768211455" + ), + Ordering::Greater + ); + + // Iterator advancement verification + let mut a_iter = "123abc".chars().peekable(); + let mut b_iter = "456def".chars().peekable(); + + compare_numeric_segments(&mut a_iter, &mut b_iter); + + assert_eq!(a_iter.collect::(), "abc"); + assert_eq!(b_iter.collect::(), "def"); + } + + #[perf] + fn test_natural_sort() { + // Basic alphanumeric + assert_eq!(natural_sort("a", "b"), Ordering::Less); + assert_eq!(natural_sort("b", "a"), Ordering::Greater); + assert_eq!(natural_sort("a", "a"), Ordering::Equal); + + // Case sensitivity + assert_eq!(natural_sort("a", "A"), Ordering::Less); + assert_eq!(natural_sort("A", "a"), Ordering::Greater); + assert_eq!(natural_sort("aA", "aa"), Ordering::Greater); + assert_eq!(natural_sort("aa", "aA"), Ordering::Less); + + // Numbers + assert_eq!(natural_sort("1", "2"), Ordering::Less); + assert_eq!(natural_sort("2", "10"), Ordering::Less); + assert_eq!(natural_sort("02", "10"), Ordering::Less); + assert_eq!(natural_sort("02", "2"), Ordering::Greater); + + // Mixed alphanumeric + assert_eq!(natural_sort("a1", "a2"), Ordering::Less); + assert_eq!(natural_sort("a2", "a10"), Ordering::Less); + assert_eq!(natural_sort("a02", "a2"), Ordering::Greater); + assert_eq!(natural_sort("a1b", "a1c"), Ordering::Less); + + // Multiple numeric segments + assert_eq!(natural_sort("1a2", "1a10"), Ordering::Less); + assert_eq!(natural_sort("1a10", "1a2"), Ordering::Greater); + assert_eq!(natural_sort("2a1", "10a1"), Ordering::Less); + + // Special characters + assert_eq!(natural_sort("a-1", "a-2"), Ordering::Less); + assert_eq!(natural_sort("a_1", "a_2"), Ordering::Less); + assert_eq!(natural_sort("a.1", "a.2"), Ordering::Less); + + // Unicode + assert_eq!(natural_sort("文1", "文2"), Ordering::Less); + assert_eq!(natural_sort("文2", "文10"), Ordering::Less); + assert_eq!(natural_sort("🔤1", "🔤2"), Ordering::Less); + + // Empty and special cases + assert_eq!(natural_sort("", ""), Ordering::Equal); + assert_eq!(natural_sort("", "a"), Ordering::Less); + assert_eq!(natural_sort("a", ""), Ordering::Greater); + assert_eq!(natural_sort(" ", " "), Ordering::Less); + + // Mixed everything + assert_eq!(natural_sort("File-1.txt", "File-2.txt"), Ordering::Less); + assert_eq!(natural_sort("File-02.txt", "File-2.txt"), Ordering::Greater); + assert_eq!(natural_sort("File-2.txt", "File-10.txt"), Ordering::Less); + assert_eq!(natural_sort("File_A1", "File_A2"), Ordering::Less); + assert_eq!(natural_sort("File_a1", "File_A1"), Ordering::Less); + } + + #[perf] + fn test_compare_paths() { + // Helper function for cleaner tests + fn compare(a: &str, is_a_file: bool, b: &str, is_b_file: bool) -> Ordering { + compare_paths((Path::new(a), is_a_file), (Path::new(b), is_b_file)) + } + + // Basic path comparison + assert_eq!(compare("a", true, "b", true), Ordering::Less); + assert_eq!(compare("b", true, "a", true), Ordering::Greater); + assert_eq!(compare("a", true, "a", true), Ordering::Equal); + + // Files vs Directories + assert_eq!(compare("a", true, "a", false), Ordering::Greater); + assert_eq!(compare("a", false, "a", true), Ordering::Less); + assert_eq!(compare("b", false, "a", true), Ordering::Less); + + // Extensions + assert_eq!(compare("a.txt", true, "a.md", true), Ordering::Greater); + assert_eq!(compare("a.md", true, "a.txt", true), Ordering::Less); + assert_eq!(compare("a", true, "a.txt", true), Ordering::Less); + + // Nested paths + assert_eq!(compare("dir/a", true, "dir/b", true), Ordering::Less); + assert_eq!(compare("dir1/a", true, "dir2/a", true), Ordering::Less); + assert_eq!(compare("dir/sub/a", true, "dir/a", true), Ordering::Less); + + // Case sensitivity in paths + assert_eq!( + compare("Dir/file", true, "dir/file", true), + Ordering::Greater + ); + assert_eq!( + compare("dir/File", true, "dir/file", true), + Ordering::Greater + ); + assert_eq!(compare("dir/file", true, "Dir/File", true), Ordering::Less); + + // Hidden files and special names + assert_eq!(compare(".hidden", true, "visible", true), Ordering::Less); + assert_eq!(compare("_special", true, "normal", true), Ordering::Less); + assert_eq!(compare(".config", false, ".data", false), Ordering::Less); + + // Mixed numeric paths + assert_eq!( + compare("dir1/file", true, "dir2/file", true), + Ordering::Less + ); + assert_eq!( + compare("dir2/file", true, "dir10/file", true), + Ordering::Less + ); + assert_eq!( + compare("dir02/file", true, "dir2/file", true), + Ordering::Greater + ); + + // Root paths + assert_eq!(compare("/a", true, "/b", true), Ordering::Less); + assert_eq!(compare("/", false, "/a", true), Ordering::Less); + + // Complex real-world examples + assert_eq!( + compare("project/src/main.rs", true, "project/src/lib.rs", true), + Ordering::Greater + ); + assert_eq!( + compare( + "project/tests/test_1.rs", + true, + "project/tests/test_2.rs", + true + ), + Ordering::Less + ); + assert_eq!( + compare( + "project/v1.0.0/README.md", + true, + "project/v1.10.0/README.md", + true + ), + Ordering::Less + ); + } + + #[perf] + fn test_natural_sort_case_sensitivity() { + std::thread::sleep(std::time::Duration::from_millis(100)); + // Same letter different case - lowercase should come first + assert_eq!(natural_sort("a", "A"), Ordering::Less); + assert_eq!(natural_sort("A", "a"), Ordering::Greater); + assert_eq!(natural_sort("a", "a"), Ordering::Equal); + assert_eq!(natural_sort("A", "A"), Ordering::Equal); + + // Mixed case strings + assert_eq!(natural_sort("aaa", "AAA"), Ordering::Less); + assert_eq!(natural_sort("AAA", "aaa"), Ordering::Greater); + assert_eq!(natural_sort("aAa", "AaA"), Ordering::Less); + + // Different letters + assert_eq!(natural_sort("a", "b"), Ordering::Less); + assert_eq!(natural_sort("A", "b"), Ordering::Less); + assert_eq!(natural_sort("a", "B"), Ordering::Less); + } + + #[perf] + fn test_natural_sort_with_numbers() { + // Basic number ordering + assert_eq!(natural_sort("file1", "file2"), Ordering::Less); + assert_eq!(natural_sort("file2", "file10"), Ordering::Less); + assert_eq!(natural_sort("file10", "file2"), Ordering::Greater); + + // Numbers in different positions + assert_eq!(natural_sort("1file", "2file"), Ordering::Less); + assert_eq!(natural_sort("file1text", "file2text"), Ordering::Less); + assert_eq!(natural_sort("text1file", "text2file"), Ordering::Less); + + // Multiple numbers in string + assert_eq!(natural_sort("file1-2", "file1-10"), Ordering::Less); + assert_eq!(natural_sort("2-1file", "10-1file"), Ordering::Less); + + // Leading zeros + assert_eq!(natural_sort("file002", "file2"), Ordering::Greater); + assert_eq!(natural_sort("file002", "file10"), Ordering::Less); + + // Very large numbers + assert_eq!( + natural_sort("file999999999999999999999", "file999999999999999999998"), + Ordering::Greater + ); + + // u128 edge cases + + // Numbers near u128::MAX (340,282,366,920,938,463,463,374,607,431,768,211,455) + assert_eq!( + natural_sort( + "file340282366920938463463374607431768211454", + "file340282366920938463463374607431768211455" + ), + Ordering::Less + ); + + // Equal length numbers that overflow u128 + assert_eq!( + natural_sort( + "file340282366920938463463374607431768211456", + "file340282366920938463463374607431768211455" + ), + Ordering::Greater + ); + + // Different length numbers that overflow u128 + assert_eq!( + natural_sort( + "file3402823669209384634633746074317682114560", + "file340282366920938463463374607431768211455" + ), + Ordering::Greater + ); + + // Leading zeros with numbers near u128::MAX + assert_eq!( + natural_sort( + "file0340282366920938463463374607431768211455", + "file340282366920938463463374607431768211455" + ), + Ordering::Greater + ); + + // Very large numbers with different lengths (both overflow u128) + assert_eq!( + natural_sort( + "file999999999999999999999999999999999999999999999999", + "file9999999999999999999999999999999999999999999999999" + ), + Ordering::Less + ); + + // Mixed case with numbers + assert_eq!(natural_sort("File1", "file2"), Ordering::Greater); + assert_eq!(natural_sort("file1", "File2"), Ordering::Less); + } + + #[perf] + fn test_natural_sort_edge_cases() { + // Empty strings + assert_eq!(natural_sort("", ""), Ordering::Equal); + assert_eq!(natural_sort("", "a"), Ordering::Less); + assert_eq!(natural_sort("a", ""), Ordering::Greater); + + // Special characters + assert_eq!(natural_sort("file-1", "file_1"), Ordering::Less); + assert_eq!(natural_sort("file.1", "file_1"), Ordering::Less); + assert_eq!(natural_sort("file 1", "file_1"), Ordering::Less); + + // Unicode characters + // 9312 vs 9313 + assert_eq!(natural_sort("file①", "file②"), Ordering::Less); + // 9321 vs 9313 + assert_eq!(natural_sort("file⑩", "file②"), Ordering::Greater); + // 28450 vs 23383 + assert_eq!(natural_sort("file漢", "file字"), Ordering::Greater); + + // Mixed alphanumeric with special chars + assert_eq!(natural_sort("file-1a", "file-1b"), Ordering::Less); + assert_eq!(natural_sort("file-1.2", "file-1.10"), Ordering::Less); + assert_eq!(natural_sort("file-1.10", "file-1.2"), Ordering::Greater); + } + + #[test] + fn test_multiple_extensions() { + // No extensions + let path = Path::new("/a/b/c/file_name"); + assert_eq!(path.multiple_extensions(), None); + + // Only one extension + let path = Path::new("/a/b/c/file_name.tsx"); + assert_eq!(path.multiple_extensions(), None); + + // Stories sample extension + let path = Path::new("/a/b/c/file_name.stories.tsx"); + assert_eq!(path.multiple_extensions(), Some("stories.tsx".to_string())); + + // Longer sample extension + let path = Path::new("/a/b/c/long.app.tar.gz"); + assert_eq!(path.multiple_extensions(), Some("app.tar.gz".to_string())); + } + + #[test] + fn test_strip_path_suffix() { + let base = Path::new("/a/b/c/file_name"); + let suffix = Path::new("file_name"); + assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a/b/c"))); + + let base = Path::new("/a/b/c/file_name.tsx"); + let suffix = Path::new("file_name.tsx"); + assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a/b/c"))); + + let base = Path::new("/a/b/c/file_name.stories.tsx"); + let suffix = Path::new("c/file_name.stories.tsx"); + assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a/b"))); + + let base = Path::new("/a/b/c/long.app.tar.gz"); + let suffix = Path::new("b/c/long.app.tar.gz"); + assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a"))); + + let base = Path::new("/a/b/c/long.app.tar.gz"); + let suffix = Path::new("/a/b/c/long.app.tar.gz"); + assert_eq!(strip_path_suffix(base, suffix), Some(Path::new(""))); + + let base = Path::new("/a/b/c/long.app.tar.gz"); + let suffix = Path::new("/a/b/c/no_match.app.tar.gz"); + assert_eq!(strip_path_suffix(base, suffix), None); + + let base = Path::new("/a/b/c/long.app.tar.gz"); + let suffix = Path::new("app.tar.gz"); + assert_eq!(strip_path_suffix(base, suffix), None); + } +} diff --git a/third_party/gpui_support/gpui_util/src/redact.rs b/third_party/gpui_support/gpui_util/src/redact.rs new file mode 100644 index 0000000..6b297df --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/redact.rs @@ -0,0 +1,15 @@ +/// Whether a given environment variable name should have its value redacted +pub fn should_redact(env_var_name: &str) -> bool { + const REDACTED_SUFFIXES: &[&str] = &[ + "KEY", + "TOKEN", + "PASSWORD", + "SECRET", + "PASS", + "CREDENTIALS", + "LICENSE", + ]; + REDACTED_SUFFIXES + .iter() + .any(|suffix| env_var_name.ends_with(suffix)) +} diff --git a/third_party/gpui_support/gpui_util/src/rel_path.rs b/third_party/gpui_support/gpui_util/src/rel_path.rs new file mode 100644 index 0000000..b360297 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/rel_path.rs @@ -0,0 +1,579 @@ +use crate::paths::{PathStyle, is_absolute}; +use anyhow::{Context as _, Result, anyhow}; +use serde::{Deserialize, Serialize}; +use std::{ + borrow::{Borrow, Cow}, + fmt, + ops::Deref, + path::{Path, PathBuf}, + sync::Arc, +}; + +/// A file system path that is guaranteed to be relative and normalized. +/// +/// This type can be used to represent paths in a uniform way, regardless of +/// whether they refer to Windows or POSIX file systems, and regardless of +/// the host platform. +/// +/// Internally, paths are stored in POSIX ('/'-delimited) format, but they can +/// be displayed in either POSIX or Windows format. +/// +/// Relative paths are also guaranteed to be valid unicode. +#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, Serialize)] +pub struct RelPath(str); + +/// An owned representation of a file system path that is guaranteed to be +/// relative and normalized. +/// +/// This type is to [`RelPath`] as [`std::path::PathBuf`] is to [`std::path::Path`] +#[derive(Clone, Serialize, Deserialize)] +pub struct RelPathBuf(String); + +impl RelPath { + /// Creates an empty [`RelPath`]. + pub fn empty() -> &'static Self { + Self::new_unchecked("") + } + + /// Converts a path with a given style into a [`RelPath`]. + /// + /// Returns an error if the path is absolute, or is not valid unicode. + /// + /// This method will normalize the path by removing `.` components, + /// processing `..` components, and removing trailing separators. It does + /// not allocate unless it's necessary to reformat the path. + #[track_caller] + pub fn new<'a>(path: &'a Path, path_style: PathStyle) -> Result> { + let mut path = path.to_str().context("non utf-8 path")?; + + let (prefixes, suffixes): (&[_], &[_]) = match path_style { + PathStyle::Posix => (&["./"], &['/']), + PathStyle::Windows => (&["./", ".\\"], &['/', '\\']), + }; + + while prefixes.iter().any(|prefix| path.starts_with(prefix)) { + path = &path[prefixes[0].len()..]; + } + while let Some(prefix) = path.strip_suffix(suffixes) + && !prefix.is_empty() + { + path = prefix; + } + + if is_absolute(&path, path_style) { + return Err(anyhow!("absolute path not allowed: {path:?}")); + } + + let mut string = Cow::Borrowed(path); + if path_style == PathStyle::Windows && path.contains('\\') { + string = Cow::Owned(string.as_ref().replace('\\', "/")) + } + + let mut result = match string { + Cow::Borrowed(string) => Cow::Borrowed(Self::new_unchecked(string)), + Cow::Owned(string) => Cow::Owned(RelPathBuf(string)), + }; + + if result + .components() + .any(|component| component == "" || component == "." || component == "..") + { + let mut normalized = RelPathBuf::new(); + for component in result.components() { + match component { + "" => {} + "." => {} + ".." => { + if !normalized.pop() { + return Err(anyhow!("path is not relative: {result:?}")); + } + } + other => normalized.push(RelPath::new_unchecked(other)), + } + } + result = Cow::Owned(normalized) + } + + Ok(result) + } + + /// Converts a path that is already normalized and uses '/' separators + /// into a [`RelPath`] . + /// + /// Returns an error if the path is not already in the correct format. + #[track_caller] + pub fn unix + ?Sized>(path: &S) -> anyhow::Result<&Self> { + let path = path.as_ref(); + match Self::new(path, PathStyle::Posix)? { + Cow::Borrowed(path) => Ok(path), + Cow::Owned(_) => Err(anyhow!("invalid relative path {path:?}")), + } + } + + fn new_unchecked(s: &str) -> &Self { + // Safety: `RelPath` is a transparent wrapper around `str`. + unsafe { &*(s as *const str as *const Self) } + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn components(&self) -> RelPathComponents<'_> { + RelPathComponents(&self.0) + } + + pub fn ancestors(&self) -> RelPathAncestors<'_> { + RelPathAncestors(Some(&self.0)) + } + + pub fn file_name(&self) -> Option<&str> { + self.components().next_back() + } + + pub fn file_stem(&self) -> Option<&str> { + Some(self.as_std_path().file_stem()?.to_str().unwrap()) + } + + pub fn extension(&self) -> Option<&str> { + Some(self.as_std_path().extension()?.to_str().unwrap()) + } + + pub fn parent(&self) -> Option<&Self> { + let mut components = self.components(); + components.next_back()?; + Some(components.rest()) + } + + pub fn starts_with(&self, other: &Self) -> bool { + self.strip_prefix(other).is_ok() + } + + pub fn ends_with(&self, other: &Self) -> bool { + if let Some(suffix) = self.0.strip_suffix(&other.0) { + if suffix.ends_with('/') { + return true; + } else if suffix.is_empty() { + return true; + } + } + false + } + + pub fn strip_prefix<'a>(&'a self, other: &Self) -> Result<&'a Self> { + if other.is_empty() { + return Ok(self); + } + if let Some(suffix) = self.0.strip_prefix(&other.0) { + if let Some(suffix) = suffix.strip_prefix('/') { + return Ok(Self::new_unchecked(suffix)); + } else if suffix.is_empty() { + return Ok(Self::empty()); + } + } + Err(anyhow!("failed to strip prefix: {other:?} from {self:?}")) + } + + pub fn len(&self) -> usize { + self.0.matches('/').count() + 1 + } + + pub fn last_n_components(&self, count: usize) -> Option<&Self> { + let len = self.len(); + if len >= count { + let mut components = self.components(); + for _ in 0..(len - count) { + components.next()?; + } + Some(components.rest()) + } else { + None + } + } + + pub fn join(&self, other: &Self) -> Arc { + let result = if self.0.is_empty() { + Cow::Borrowed(&other.0) + } else if other.0.is_empty() { + Cow::Borrowed(&self.0) + } else { + Cow::Owned(format!("{}/{}", &self.0, &other.0)) + }; + Arc::from(Self::new_unchecked(result.as_ref())) + } + + pub fn to_rel_path_buf(&self) -> RelPathBuf { + RelPathBuf(self.0.to_string()) + } + + pub fn into_arc(&self) -> Arc { + Arc::from(self) + } + + /// Convert the path into the wire representation. + pub fn to_proto(&self) -> String { + self.as_unix_str().to_owned() + } + + /// Load the path from its wire representation. + pub fn from_proto(path: &str) -> Result> { + Ok(Arc::from(Self::unix(path)?)) + } + + /// Convert the path into a string with the given path style. + /// + /// Whenever a path is presented to the user, it should be converted to + /// a string via this method. + pub fn display(&self, style: PathStyle) -> Cow<'_, str> { + match style { + PathStyle::Posix => Cow::Borrowed(&self.0), + PathStyle::Windows => Cow::Owned(self.0.replace('/', "\\")), + } + } + + /// Get the internal unix-style representation of the path. + /// + /// This should not be shown to the user. + pub fn as_unix_str(&self) -> &str { + &self.0 + } + + /// Interprets the path as a [`std::path::Path`], suitable for file system calls. + /// + /// This is guaranteed to be a valid path regardless of the host platform, because + /// the `/` is accepted as a path separator on windows. + /// + /// This should not be shown to the user. + pub fn as_std_path(&self) -> &Path { + Path::new(&self.0) + } +} + +impl ToOwned for RelPath { + type Owned = RelPathBuf; + + fn to_owned(&self) -> Self::Owned { + self.to_rel_path_buf() + } +} + +impl Borrow for RelPathBuf { + fn borrow(&self) -> &RelPath { + self.as_rel_path() + } +} + +impl PartialOrd for RelPath { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for RelPath { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.components().cmp(other.components()) + } +} + +impl fmt::Debug for RelPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Debug for RelPathBuf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl RelPathBuf { + pub fn new() -> Self { + Self(String::new()) + } + + pub fn pop(&mut self) -> bool { + if let Some(ix) = self.0.rfind('/') { + self.0.truncate(ix); + true + } else if !self.is_empty() { + self.0.clear(); + true + } else { + false + } + } + + pub fn push(&mut self, path: &RelPath) { + if !self.is_empty() { + self.0.push('/'); + } + self.0.push_str(&path.0); + } + + pub fn as_rel_path(&self) -> &RelPath { + RelPath::new_unchecked(self.0.as_str()) + } + + pub fn set_extension(&mut self, extension: &str) -> bool { + if let Some(filename) = self.file_name() { + let mut filename = PathBuf::from(filename); + filename.set_extension(extension); + self.pop(); + self.0.push_str(filename.to_str().unwrap()); + true + } else { + false + } + } +} + +impl Into> for RelPathBuf { + fn into(self) -> Arc { + Arc::from(self.as_rel_path()) + } +} + +impl AsRef for RelPathBuf { + fn as_ref(&self) -> &RelPath { + self.as_rel_path() + } +} + +impl Deref for RelPathBuf { + type Target = RelPath; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl<'a> From<&'a RelPath> for Cow<'a, RelPath> { + fn from(value: &'a RelPath) -> Self { + Self::Borrowed(value) + } +} + +impl From<&RelPath> for Arc { + fn from(rel_path: &RelPath) -> Self { + let bytes: Arc = Arc::from(&rel_path.0); + unsafe { Arc::from_raw(Arc::into_raw(bytes) as *const RelPath) } + } +} + +#[cfg(any(test, feature = "test-support"))] +#[track_caller] +pub fn rel_path(path: &str) -> &RelPath { + RelPath::unix(path).unwrap() +} + +impl PartialEq for RelPath { + fn eq(&self, other: &str) -> bool { + self.0 == *other + } +} + +pub struct RelPathComponents<'a>(&'a str); + +pub struct RelPathAncestors<'a>(Option<&'a str>); + +const SEPARATOR: char = '/'; + +impl<'a> RelPathComponents<'a> { + pub fn rest(&self) -> &'a RelPath { + RelPath::new_unchecked(self.0) + } +} + +impl<'a> Iterator for RelPathComponents<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option { + if let Some(sep_ix) = self.0.find(SEPARATOR) { + let (head, tail) = self.0.split_at(sep_ix); + self.0 = &tail[1..]; + Some(head) + } else if self.0.is_empty() { + None + } else { + let result = self.0; + self.0 = ""; + Some(result) + } + } +} + +impl<'a> Iterator for RelPathAncestors<'a> { + type Item = &'a RelPath; + + fn next(&mut self) -> Option { + let result = self.0?; + if let Some(sep_ix) = result.rfind(SEPARATOR) { + self.0 = Some(&result[..sep_ix]); + } else if !result.is_empty() { + self.0 = Some(""); + } else { + self.0 = None; + } + Some(RelPath::new_unchecked(result)) + } +} + +impl<'a> DoubleEndedIterator for RelPathComponents<'a> { + fn next_back(&mut self) -> Option { + if let Some(sep_ix) = self.0.rfind(SEPARATOR) { + let (head, tail) = self.0.split_at(sep_ix); + self.0 = head; + Some(&tail[1..]) + } else if self.0.is_empty() { + None + } else { + let result = self.0; + self.0 = ""; + Some(result) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itertools::Itertools; + use pretty_assertions::assert_matches; + + #[test] + fn test_rel_path_new() { + assert!(RelPath::new(Path::new("/"), PathStyle::local()).is_err()); + assert!(RelPath::new(Path::new("//"), PathStyle::local()).is_err()); + assert!(RelPath::new(Path::new("/foo/"), PathStyle::local()).is_err()); + + let path = RelPath::new("foo/".as_ref(), PathStyle::local()).unwrap(); + assert_eq!(path, rel_path("foo").into()); + assert_matches!(path, Cow::Borrowed(_)); + + let path = RelPath::new("foo\\".as_ref(), PathStyle::Windows).unwrap(); + assert_eq!(path, rel_path("foo").into()); + assert_matches!(path, Cow::Borrowed(_)); + + assert_eq!( + RelPath::new("foo/bar/../baz/./quux/".as_ref(), PathStyle::local()) + .unwrap() + .as_ref(), + rel_path("foo/baz/quux") + ); + + let path = RelPath::new("./foo/bar".as_ref(), PathStyle::Posix).unwrap(); + assert_eq!(path.as_ref(), rel_path("foo/bar")); + assert_matches!(path, Cow::Borrowed(_)); + + let path = RelPath::new(".\\foo".as_ref(), PathStyle::Windows).unwrap(); + assert_eq!(path, rel_path("foo").into()); + assert_matches!(path, Cow::Borrowed(_)); + + let path = RelPath::new("./.\\./foo/\\/".as_ref(), PathStyle::Windows).unwrap(); + assert_eq!(path, rel_path("foo").into()); + assert_matches!(path, Cow::Borrowed(_)); + + let path = RelPath::new("foo/./bar".as_ref(), PathStyle::Posix).unwrap(); + assert_eq!(path.as_ref(), rel_path("foo/bar")); + assert_matches!(path, Cow::Owned(_)); + + let path = RelPath::new("./foo/bar".as_ref(), PathStyle::Windows).unwrap(); + assert_eq!(path.as_ref(), rel_path("foo/bar")); + assert_matches!(path, Cow::Borrowed(_)); + + let path = RelPath::new(".\\foo\\bar".as_ref(), PathStyle::Windows).unwrap(); + assert_eq!(path.as_ref(), rel_path("foo/bar")); + assert_matches!(path, Cow::Owned(_)); + } + + #[test] + fn test_rel_path_components() { + let path = rel_path("foo/bar/baz"); + assert_eq!( + path.components().collect::>(), + vec!["foo", "bar", "baz"] + ); + assert_eq!( + path.components().rev().collect::>(), + vec!["baz", "bar", "foo"] + ); + + let path = rel_path(""); + let mut components = path.components(); + assert_eq!(components.next(), None); + } + + #[test] + fn test_rel_path_ancestors() { + let path = rel_path("foo/bar/baz"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next(), Some(rel_path("foo/bar/baz"))); + assert_eq!(ancestors.next(), Some(rel_path("foo/bar"))); + assert_eq!(ancestors.next(), Some(rel_path("foo"))); + assert_eq!(ancestors.next(), Some(rel_path(""))); + assert_eq!(ancestors.next(), None); + + let path = rel_path("foo"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next(), Some(rel_path("foo"))); + assert_eq!(ancestors.next(), Some(RelPath::empty())); + assert_eq!(ancestors.next(), None); + + let path = RelPath::empty(); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next(), Some(RelPath::empty())); + assert_eq!(ancestors.next(), None); + } + + #[test] + fn test_rel_path_parent() { + assert_eq!(rel_path("foo/bar/baz").parent(), Some(rel_path("foo/bar"))); + assert_eq!(rel_path("foo").parent(), Some(RelPath::empty())); + assert_eq!(rel_path("").parent(), None); + } + + #[test] + fn test_rel_path_partial_ord_is_compatible_with_std() { + let test_cases = ["a/b/c", "relative/path/with/dot.", "relative/path/with.dot"]; + for [lhs, rhs] in test_cases.iter().array_combinations::<2>() { + assert_eq!( + Path::new(lhs).cmp(Path::new(rhs)), + RelPath::unix(lhs) + .unwrap() + .cmp(&RelPath::unix(rhs).unwrap()) + ); + } + } + + #[test] + fn test_strip_prefix() { + let parent = rel_path(""); + let child = rel_path(".foo"); + + assert!(child.starts_with(parent)); + assert_eq!(child.strip_prefix(parent).unwrap(), child); + } + + #[test] + fn test_rel_path_constructors_absolute_path() { + assert!(RelPath::new(Path::new("/a/b"), PathStyle::Windows).is_err()); + assert!(RelPath::new(Path::new("\\a\\b"), PathStyle::Windows).is_err()); + assert!(RelPath::new(Path::new("/a/b"), PathStyle::Posix).is_err()); + assert!(RelPath::new(Path::new("C:/a/b"), PathStyle::Windows).is_err()); + assert!(RelPath::new(Path::new("C:\\a\\b"), PathStyle::Windows).is_err()); + assert!(RelPath::new(Path::new("C:/a/b"), PathStyle::Posix).is_ok()); + } + + #[test] + fn test_pop() { + let mut path = rel_path("a/b").to_rel_path_buf(); + path.pop(); + assert_eq!(path.as_rel_path().as_unix_str(), "a"); + path.pop(); + assert_eq!(path.as_rel_path().as_unix_str(), ""); + path.pop(); + assert_eq!(path.as_rel_path().as_unix_str(), ""); + } +} diff --git a/third_party/gpui_support/gpui_util/src/schemars.rs b/third_party/gpui_support/gpui_util/src/schemars.rs new file mode 100644 index 0000000..9314eda --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/schemars.rs @@ -0,0 +1,55 @@ +use schemars::{JsonSchema, transform::transform_subschemas}; + +const DEFS_PATH: &str = "#/$defs/"; + +/// Replaces the JSON schema definition for some type if it is in use (in the definitions list), and +/// returns a reference to it. +/// +/// This asserts that JsonSchema::schema_name() + "2" does not exist because this indicates that +/// there are multiple types that use this name, and unfortunately schemars APIs do not support +/// resolving this ambiguity - see +/// +/// This takes a closure for `schema` because some settings types are not available on the remote +/// server, and so will crash when attempting to access e.g. GlobalThemeRegistry. +pub fn replace_subschema( + generator: &mut schemars::SchemaGenerator, + schema: impl Fn() -> schemars::Schema, +) -> schemars::Schema { + let schema_name = T::schema_name(); + let definitions = generator.definitions_mut(); + assert!(!definitions.contains_key(&format!("{schema_name}2"))); + assert!(definitions.contains_key(schema_name.as_ref())); + definitions.insert(schema_name.to_string(), schema().to_value()); + schemars::Schema::new_ref(format!("{DEFS_PATH}{schema_name}")) +} + +/// Adds a new JSON schema definition and returns a reference to it. **Panics** if the name is +/// already in use. +pub fn add_new_subschema( + generator: &mut schemars::SchemaGenerator, + name: &str, + schema: serde_json::Value, +) -> schemars::Schema { + let old_definition = generator.definitions_mut().insert(name.to_string(), schema); + assert_eq!(old_definition, None); + schemars::Schema::new_ref(format!("{DEFS_PATH}{name}")) +} + +/// Defaults `additionalProperties` to `true`, as if `#[schemars(deny_unknown_fields)]` was on every +/// struct. Skips structs that have `additionalProperties` set (such as if #[serde(flatten)] is used +/// on a map). +#[derive(Clone)] +pub struct DefaultDenyUnknownFields; + +impl schemars::transform::Transform for DefaultDenyUnknownFields { + fn transform(&mut self, schema: &mut schemars::Schema) { + if let Some(object) = schema.as_object_mut() + && object.contains_key("properties") + && !object.contains_key("additionalProperties") + && !object.contains_key("unevaluatedProperties") + { + object.insert("additionalProperties".to_string(), false.into()); + } + transform_subschemas(self, schema); + } +} diff --git a/third_party/gpui_support/gpui_util/src/serde.rs b/third_party/gpui_support/gpui_util/src/serde.rs new file mode 100644 index 0000000..4aa4bb1 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/serde.rs @@ -0,0 +1,7 @@ +pub const fn default_true() -> bool { + true +} + +pub fn is_default(value: &T) -> bool { + *value == T::default() +} diff --git a/third_party/gpui_support/gpui_util/src/shell.rs b/third_party/gpui_support/gpui_util/src/shell.rs new file mode 100644 index 0000000..22e07ac --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/shell.rs @@ -0,0 +1,406 @@ +use serde::{Deserialize, Serialize}; +use std::{borrow::Cow, fmt, path::Path, sync::LazyLock}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum ShellKind { + #[default] + Posix, + Csh, + Tcsh, + Rc, + Fish, + PowerShell, + Nushell, + Cmd, + Xonsh, +} + +pub fn get_system_shell() -> String { + if cfg!(windows) { + get_windows_system_shell() + } else { + std::env::var("SHELL").unwrap_or("/bin/sh".to_string()) + } +} + +pub fn get_default_system_shell() -> String { + if cfg!(windows) { + get_windows_system_shell() + } else { + "/bin/sh".to_string() + } +} + +/// Get the default system shell, preferring git-bash on Windows. +pub fn get_default_system_shell_preferring_bash() -> String { + if cfg!(windows) { + get_windows_git_bash().unwrap_or_else(|| get_windows_system_shell()) + } else { + "/bin/sh".to_string() + } +} + +pub fn get_windows_git_bash() -> Option { + static GIT_BASH: LazyLock> = LazyLock::new(|| { + // /path/to/git/cmd/git.exe/../../bin/bash.exe + let git = which::which("git").ok()?; + let git_bash = git.parent()?.parent()?.join("bin").join("bash.exe"); + if git_bash.is_file() { + log::info!("Found git-bash at {}", git_bash.display()); + Some(git_bash.to_string_lossy().to_string()) + } else { + None + } + }); + + (*GIT_BASH).clone() +} + +pub fn get_windows_system_shell() -> String { + use std::path::PathBuf; + + fn find_pwsh_in_programfiles(find_alternate: bool, find_preview: bool) -> Option { + #[cfg(target_pointer_width = "64")] + let env_var = if find_alternate { + "ProgramFiles(x86)" + } else { + "ProgramFiles" + }; + + #[cfg(target_pointer_width = "32")] + let env_var = if find_alternate { + "ProgramW6432" + } else { + "ProgramFiles" + }; + + let install_base_dir = PathBuf::from(std::env::var_os(env_var)?).join("PowerShell"); + install_base_dir + .read_dir() + .ok()? + .filter_map(Result::ok) + .filter(|entry| matches!(entry.file_type(), Ok(ft) if ft.is_dir())) + .filter_map(|entry| { + let dir_name = entry.file_name(); + let dir_name = dir_name.to_string_lossy(); + + let version = if find_preview { + let dash_index = dir_name.find('-')?; + if &dir_name[dash_index + 1..] != "preview" { + return None; + }; + dir_name[..dash_index].parse::().ok()? + } else { + dir_name.parse::().ok()? + }; + + let exe_path = entry.path().join("pwsh.exe"); + if exe_path.exists() { + Some((version, exe_path)) + } else { + None + } + }) + .max_by_key(|(version, _)| *version) + .map(|(_, path)| path) + } + + fn find_pwsh_in_msix(find_preview: bool) -> Option { + let msix_app_dir = + PathBuf::from(std::env::var_os("LOCALAPPDATA")?).join("Microsoft\\WindowsApps"); + if !msix_app_dir.exists() { + return None; + } + + let prefix = if find_preview { + "Microsoft.PowerShellPreview_" + } else { + "Microsoft.PowerShell_" + }; + msix_app_dir + .read_dir() + .ok()? + .filter_map(|entry| { + let entry = entry.ok()?; + if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) { + return None; + } + + if !entry.file_name().to_string_lossy().starts_with(prefix) { + return None; + } + + let exe_path = entry.path().join("pwsh.exe"); + exe_path.exists().then_some(exe_path) + }) + .next() + } + + fn find_pwsh_in_scoop() -> Option { + let pwsh_exe = + PathBuf::from(std::env::var_os("USERPROFILE")?).join("scoop\\shims\\pwsh.exe"); + pwsh_exe.exists().then_some(pwsh_exe) + } + + static SYSTEM_SHELL: LazyLock = LazyLock::new(|| { + find_pwsh_in_programfiles(false, false) + .or_else(|| find_pwsh_in_programfiles(true, false)) + .or_else(|| find_pwsh_in_msix(false)) + .or_else(|| find_pwsh_in_programfiles(false, true)) + .or_else(|| find_pwsh_in_msix(true)) + .or_else(|| find_pwsh_in_programfiles(true, true)) + .or_else(find_pwsh_in_scoop) + .map(|p| p.to_string_lossy().into_owned()) + .unwrap_or("powershell.exe".to_string()) + }); + + (*SYSTEM_SHELL).clone() +} + +impl fmt::Display for ShellKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ShellKind::Posix => write!(f, "sh"), + ShellKind::Csh => write!(f, "csh"), + ShellKind::Tcsh => write!(f, "tcsh"), + ShellKind::Fish => write!(f, "fish"), + ShellKind::PowerShell => write!(f, "powershell"), + ShellKind::Nushell => write!(f, "nu"), + ShellKind::Cmd => write!(f, "cmd"), + ShellKind::Rc => write!(f, "rc"), + ShellKind::Xonsh => write!(f, "xonsh"), + } + } +} + +impl ShellKind { + pub fn system() -> Self { + Self::new(&get_system_shell(), cfg!(windows)) + } + + pub fn new(program: impl AsRef, is_windows: bool) -> Self { + let program = program.as_ref(); + let program = program + .file_stem() + .unwrap_or_else(|| program.as_os_str()) + .to_string_lossy(); + + if program == "powershell" || program == "pwsh" { + ShellKind::PowerShell + } else if program == "cmd" { + ShellKind::Cmd + } else if program == "nu" { + ShellKind::Nushell + } else if program == "fish" { + ShellKind::Fish + } else if program == "csh" { + ShellKind::Csh + } else if program == "tcsh" { + ShellKind::Tcsh + } else if program == "rc" { + ShellKind::Rc + } else if program == "xonsh" { + ShellKind::Xonsh + } else if program == "sh" || program == "bash" { + ShellKind::Posix + } else { + if is_windows { + ShellKind::PowerShell + } else { + // Some other shell detected, the user might install and use a + // unix-like shell. + ShellKind::Posix + } + } + } + + pub fn to_shell_variable(self, input: &str) -> String { + match self { + Self::PowerShell => Self::to_powershell_variable(input), + Self::Cmd => Self::to_cmd_variable(input), + Self::Posix => input.to_owned(), + Self::Fish => input.to_owned(), + Self::Csh => input.to_owned(), + Self::Tcsh => input.to_owned(), + Self::Rc => input.to_owned(), + Self::Nushell => Self::to_nushell_variable(input), + Self::Xonsh => input.to_owned(), + } + } + + fn to_cmd_variable(input: &str) -> String { + if let Some(var_str) = input.strip_prefix("${") { + if var_str.find(':').is_none() { + // If the input starts with "${", remove the trailing "}" + format!("%{}%", &var_str[..var_str.len() - 1]) + } else { + // `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation, + // which will result in the task failing to run in such cases. + input.into() + } + } else if let Some(var_str) = input.strip_prefix('$') { + // If the input starts with "$", directly append to "$env:" + format!("%{}%", var_str) + } else { + // If no prefix is found, return the input as is + input.into() + } + } + + fn to_powershell_variable(input: &str) -> String { + if let Some(var_str) = input.strip_prefix("${") { + if var_str.find(':').is_none() { + // If the input starts with "${", remove the trailing "}" + format!("$env:{}", &var_str[..var_str.len() - 1]) + } else { + // `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation, + // which will result in the task failing to run in such cases. + input.into() + } + } else if let Some(var_str) = input.strip_prefix('$') { + // If the input starts with "$", directly append to "$env:" + format!("$env:{}", var_str) + } else { + // If no prefix is found, return the input as is + input.into() + } + } + + fn to_nushell_variable(input: &str) -> String { + let mut result = String::new(); + let mut source = input; + let mut is_start = true; + + loop { + match source.chars().next() { + None => return result, + Some('$') => { + source = Self::parse_nushell_var(&source[1..], &mut result, is_start); + is_start = false; + } + Some(_) => { + is_start = false; + let chunk_end = source.find('$').unwrap_or(source.len()); + let (chunk, rest) = source.split_at(chunk_end); + result.push_str(chunk); + source = rest; + } + } + } + } + + fn parse_nushell_var<'a>(source: &'a str, text: &mut String, is_start: bool) -> &'a str { + if source.starts_with("env.") { + text.push('$'); + return source; + } + + match source.chars().next() { + Some('{') => { + let source = &source[1..]; + if let Some(end) = source.find('}') { + let var_name = &source[..end]; + if !var_name.is_empty() { + if !is_start { + text.push_str("("); + } + text.push_str("$env."); + text.push_str(var_name); + if !is_start { + text.push_str(")"); + } + &source[end + 1..] + } else { + text.push_str("${}"); + &source[end + 1..] + } + } else { + text.push_str("${"); + source + } + } + Some(c) if c.is_alphabetic() || c == '_' => { + let end = source + .find(|c: char| !c.is_alphanumeric() && c != '_') + .unwrap_or(source.len()); + let var_name = &source[..end]; + if !is_start { + text.push_str("("); + } + text.push_str("$env."); + text.push_str(var_name); + if !is_start { + text.push_str(")"); + } + &source[end..] + } + _ => { + text.push('$'); + source + } + } + } + + pub fn args_for_shell(&self, interactive: bool, combined_command: String) -> Vec { + match self { + ShellKind::PowerShell => vec!["-C".to_owned(), combined_command], + ShellKind::Cmd => vec!["/C".to_owned(), combined_command], + ShellKind::Posix + | ShellKind::Nushell + | ShellKind::Fish + | ShellKind::Csh + | ShellKind::Tcsh + | ShellKind::Rc + | ShellKind::Xonsh => interactive + .then(|| "-i".to_owned()) + .into_iter() + .chain(["-c".to_owned(), combined_command]) + .collect(), + } + } + + pub const fn command_prefix(&self) -> Option { + match self { + ShellKind::PowerShell => Some('&'), + ShellKind::Nushell => Some('^'), + _ => None, + } + } + + pub const fn sequential_commands_separator(&self) -> char { + match self { + ShellKind::Cmd => '&', + _ => ';', + } + } + + pub fn try_quote<'a>(&self, arg: &'a str) -> Option> { + shlex::try_quote(arg).ok().map(|arg| match self { + // If we are running in PowerShell, we want to take extra care when escaping strings. + // In particular, we want to escape strings with a backtick (`) rather than a backslash (\). + // TODO double escaping backslashes is not necessary in PowerShell and probably CMD + ShellKind::PowerShell => Cow::Owned(arg.replace("\\\"", "`\"")), + _ => arg, + }) + } + + pub const fn activate_keyword(&self) -> &'static str { + match self { + ShellKind::Cmd => "", + ShellKind::Nushell => "overlay use", + ShellKind::PowerShell => ".", + ShellKind::Fish => "source", + ShellKind::Csh => "source", + ShellKind::Tcsh => "source", + ShellKind::Posix | ShellKind::Rc => "source", + ShellKind::Xonsh => "source", + } + } + + pub const fn clear_screen_command(&self) -> &'static str { + match self { + ShellKind::Cmd => "cls", + _ => "clear", + } + } +} diff --git a/third_party/gpui_support/gpui_util/src/shell_env.rs b/third_party/gpui_support/gpui_util/src/shell_env.rs new file mode 100644 index 0000000..a82bea1 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/shell_env.rs @@ -0,0 +1,236 @@ +use std::path::Path; + +use anyhow::{Context as _, Result}; +use collections::HashMap; + +use crate::shell::ShellKind; + +pub fn print_env() { + let env_vars: HashMap = std::env::vars().collect(); + let json = serde_json::to_string_pretty(&env_vars).unwrap_or_else(|err| { + eprintln!("Error serializing environment variables: {}", err); + std::process::exit(1); + }); + println!("{}", json); +} + +/// Capture all environment variables from the login shell in the given directory. +pub async fn capture( + shell_path: impl AsRef, + args: &[String], + directory: impl AsRef, +) -> Result> { + #[cfg(windows)] + return capture_windows(shell_path.as_ref(), args, directory.as_ref()).await; + #[cfg(unix)] + return capture_unix(shell_path.as_ref(), args, directory.as_ref()).await; +} + +#[cfg(unix)] +async fn capture_unix( + shell_path: &Path, + args: &[String], + directory: &Path, +) -> Result> { + use std::os::unix::process::CommandExt; + use std::process::Stdio; + + let zed_path = super::get_shell_safe_zed_path()?; + let shell_kind = ShellKind::new(shell_path, false); + + let mut command_string = String::new(); + let mut command = std::process::Command::new(shell_path); + command.args(args); + // In some shells, file descriptors greater than 2 cannot be used in interactive mode, + // so file descriptor 0 (stdin) is used instead. This impacts zsh, old bash; perhaps others. + // See: https://github.com/zed-industries/zed/pull/32136#issuecomment-2999645482 + const FD_STDIN: std::os::fd::RawFd = 0; + const FD_STDOUT: std::os::fd::RawFd = 1; + const FD_STDERR: std::os::fd::RawFd = 2; + + let (fd_num, redir) = match shell_kind { + ShellKind::Rc => (FD_STDIN, format!(">[1={}]", FD_STDIN)), // `[1=0]` + ShellKind::Nushell | ShellKind::Tcsh => (FD_STDOUT, "".to_string()), + // xonsh doesn't support redirecting to stdin, and control sequences are printed to + // stdout on startup + ShellKind::Xonsh => (FD_STDERR, "o>e".to_string()), + _ => (FD_STDIN, format!(">&{}", FD_STDIN)), // `>&0` + }; + command.stdin(Stdio::null()); + command.stdout(Stdio::piped()); + command.stderr(Stdio::piped()); + + match shell_kind { + ShellKind::Csh | ShellKind::Tcsh => { + // For csh/tcsh, login shell requires passing `-` as 0th argument (instead of `-l`) + command.arg0("-"); + } + ShellKind::Fish => { + // in fish, asdf, direnv attach to the `fish_prompt` event + command_string.push_str("emit fish_prompt;"); + command.arg("-l"); + } + _ => { + command.arg("-l"); + } + } + // cd into the directory, triggering directory specific side-effects (asdf, direnv, etc) + command_string.push_str(&format!("cd '{}';", directory.display())); + if let Some(prefix) = shell_kind.command_prefix() { + command_string.push(prefix); + } + command_string.push_str(&format!("{} --printenv {}", zed_path, redir)); + command.args(["-i", "-c", &command_string]); + + super::set_pre_exec_to_start_new_session(&mut command); + + let (env_output, process_output) = spawn_and_read_fd(command, fd_num).await?; + let env_output = String::from_utf8_lossy(&env_output); + + anyhow::ensure!( + process_output.status.success(), + "login shell exited with {}. stdout: {:?}, stderr: {:?}", + process_output.status, + String::from_utf8_lossy(&process_output.stdout), + String::from_utf8_lossy(&process_output.stderr), + ); + + // Parse the JSON output from zed --printenv + let env_map: collections::HashMap = serde_json::from_str(&env_output) + .with_context(|| "Failed to deserialize environment variables from json")?; + Ok(env_map) +} + +#[cfg(unix)] +async fn spawn_and_read_fd( + mut command: std::process::Command, + child_fd: std::os::fd::RawFd, +) -> anyhow::Result<(Vec, std::process::Output)> { + use command_fds::{CommandFdExt, FdMapping}; + use std::io::Read; + + let (mut reader, writer) = std::io::pipe()?; + + command.fd_mappings(vec![FdMapping { + parent_fd: writer.into(), + child_fd, + }])?; + + let process = smol::process::Command::from(command).spawn()?; + + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer)?; + + Ok((buffer, process.output().await?)) +} + +#[cfg(windows)] +async fn capture_windows( + shell_path: &Path, + _args: &[String], + directory: &Path, +) -> Result> { + use std::process::Stdio; + + let zed_path = + std::env::current_exe().context("Failed to determine current zed executable path.")?; + + let shell_kind = ShellKind::new(shell_path, true); + let env_output = match shell_kind { + ShellKind::Posix + | ShellKind::Csh + | ShellKind::Tcsh + | ShellKind::Rc + | ShellKind::Fish + | ShellKind::Xonsh => { + return Err(anyhow::anyhow!("unsupported shell kind")); + } + ShellKind::PowerShell => { + let output = crate::command::new_smol_command(shell_path) + .args([ + "-NonInteractive", + "-NoProfile", + "-Command", + &format!( + "Set-Location '{}'; & '{}' --printenv", + directory.display(), + zed_path.display() + ), + ]) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await?; + + anyhow::ensure!( + output.status.success(), + "PowerShell command failed with {}. stdout: {:?}, stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + output + } + ShellKind::Nushell => { + let output = crate::command::new_smol_command(shell_path) + .args([ + "-c", + &format!( + "cd '{}'; {}{} --printenv", + directory.display(), + shell_kind + .command_prefix() + .map(|prefix| prefix.to_string()) + .unwrap_or_default(), + zed_path.display() + ), + ]) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await?; + + anyhow::ensure!( + output.status.success(), + "Nushell command failed with {}. stdout: {:?}, stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + output + } + ShellKind::Cmd => { + let output = crate::command::new_smol_command(shell_path) + .args([ + "/c", + &format!( + "cd '{}'; {} --printenv", + directory.display(), + zed_path.display() + ), + ]) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await?; + + anyhow::ensure!( + output.status.success(), + "Cmd command failed with {}. stdout: {:?}, stderr: {:?}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + output + } + }; + + let env_output = String::from_utf8_lossy(&env_output.stdout); + + // Parse the JSON output from zed --printenv + serde_json::from_str(&env_output) + .with_context(|| "Failed to deserialize environment variables from json") +} diff --git a/third_party/gpui_support/gpui_util/src/size.rs b/third_party/gpui_support/gpui_util/src/size.rs new file mode 100644 index 0000000..c6ecebd --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/size.rs @@ -0,0 +1,46 @@ +pub fn format_file_size(size: u64, use_decimal: bool) -> String { + if use_decimal { + if size < 1000 { + format!("{size}B") + } else if size < 1000 * 1000 { + format!("{:.1}KB", size as f64 / 1000.0) + } else { + format!("{:.1}MB", size as f64 / (1000.0 * 1000.0)) + } + } else if size < 1024 { + format!("{size}B") + } else if size < 1024 * 1024 { + format!("{:.1}KiB", size as f64 / 1024.0) + } else { + format!("{:.1}MiB", size as f64 / (1024.0 * 1024.0)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_file_size_decimal() { + assert_eq!(format_file_size(0, true), "0B"); + assert_eq!(format_file_size(999, true), "999B"); + assert_eq!(format_file_size(1000, true), "1.0KB"); + assert_eq!(format_file_size(1500, true), "1.5KB"); + assert_eq!(format_file_size(999999, true), "1000.0KB"); + assert_eq!(format_file_size(1000000, true), "1.0MB"); + assert_eq!(format_file_size(1500000, true), "1.5MB"); + assert_eq!(format_file_size(10000000, true), "10.0MB"); + } + + #[test] + fn test_format_file_size_binary() { + assert_eq!(format_file_size(0, false), "0B"); + assert_eq!(format_file_size(1023, false), "1023B"); + assert_eq!(format_file_size(1024, false), "1.0KiB"); + assert_eq!(format_file_size(1536, false), "1.5KiB"); + assert_eq!(format_file_size(1048575, false), "1024.0KiB"); + assert_eq!(format_file_size(1048576, false), "1.0MiB"); + assert_eq!(format_file_size(1572864, false), "1.5MiB"); + assert_eq!(format_file_size(10485760, false), "10.0MiB"); + } +} diff --git a/third_party/gpui_support/gpui_util/src/test.rs b/third_party/gpui_support/gpui_util/src/test.rs new file mode 100644 index 0000000..0a251a1 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/test.rs @@ -0,0 +1,81 @@ +mod assertions; +mod marked_text; + +use git2; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; +use tempfile::TempDir; + +pub use assertions::*; +pub use marked_text::*; + +pub struct TempTree { + _temp_dir: TempDir, + path: PathBuf, +} + +impl TempTree { + pub fn new(tree: serde_json::Value) -> Self { + let dir = TempDir::new().unwrap(); + let path = std::fs::canonicalize(dir.path()).unwrap(); + write_tree(path.as_path(), tree); + + Self { + _temp_dir: dir, + path, + } + } + + pub fn path(&self) -> &Path { + self.path.as_path() + } +} + +fn write_tree(path: &Path, tree: serde_json::Value) { + use serde_json::Value; + use std::fs; + + if let Value::Object(map) = tree { + for (name, contents) in map { + let mut path = PathBuf::from(path); + path.push(name); + match contents { + Value::Object(_) => { + fs::create_dir(&path).unwrap(); + + if path.file_name() == Some(OsStr::new(".git")) { + git2::Repository::init(path.parent().unwrap()).unwrap(); + } + + write_tree(&path, contents); + } + Value::Null => { + fs::create_dir(&path).unwrap(); + } + Value::String(contents) => { + fs::write(&path, contents).unwrap(); + } + _ => { + panic!("JSON object must contain only objects, strings, or null"); + } + } + } + } else { + panic!("You must pass a JSON object to this helper") + } +} + +pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String { + let mut text = String::new(); + for row in 0..rows { + let c: char = (start_char as u32 + row as u32) as u8 as char; + let mut line = c.to_string().repeat(cols); + if row < rows - 1 { + line.push('\n'); + } + text += &line; + } + text +} diff --git a/third_party/gpui_support/gpui_util/src/test/assertions.rs b/third_party/gpui_support/gpui_util/src/test/assertions.rs new file mode 100644 index 0000000..afb1397 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/test/assertions.rs @@ -0,0 +1,62 @@ +pub enum SetEqError { + LeftMissing(T), + RightMissing(T), +} + +impl SetEqError { + pub fn map R>(self, update: F) -> SetEqError { + match self { + SetEqError::LeftMissing(missing) => SetEqError::LeftMissing(update(missing)), + SetEqError::RightMissing(missing) => SetEqError::RightMissing(update(missing)), + } + } +} + +#[macro_export] +macro_rules! set_eq { + ($left:expr,$right:expr) => {{ + use util::test::*; + + let left = $left; + let right = $right; + + let mut result = Ok(()); + for right_value in right.iter() { + if !left.contains(right_value) { + result = Err(SetEqError::LeftMissing(right_value.clone())); + break; + } + } + + if result.is_ok() { + for left_value in left.iter() { + if !right.contains(left_value) { + result = Err(SetEqError::RightMissing(left_value.clone())); + } + } + } + + result + }}; +} + +#[macro_export] +macro_rules! assert_set_eq { + ($left:expr,$right:expr) => {{ + use util::test::*; + use util::set_eq; + + let left = $left; + let right = $right; + + match set_eq!(&left, &right) { + Err(SetEqError::LeftMissing(missing)) => { + panic!("assertion failed: `(left == right)`\n left: {:?}\nright: {:?}\nleft does not contain {:?}", &left, &right, &missing); + }, + Err(SetEqError::RightMissing(missing)) => { + panic!("assertion failed: `(left == right)`\n left: {:?}\nright: {:?}\nright does not contain {:?}", &left, &right, &missing); + }, + _ => {} + } + }}; +} diff --git a/third_party/gpui_support/gpui_util/src/test/marked_text.rs b/third_party/gpui_support/gpui_util/src/test/marked_text.rs new file mode 100644 index 0000000..282a477 --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/test/marked_text.rs @@ -0,0 +1,281 @@ +use collections::HashMap; +use std::{cmp::Ordering, ops::Range}; + +/// Construct a string and a list of offsets within that string using a single +/// string containing embedded position markers. +pub fn marked_text_offsets_by( + marked_text: &str, + markers: Vec, +) -> (String, HashMap>) { + let mut extracted_markers: HashMap> = Default::default(); + let mut unmarked_text = String::new(); + + for char in marked_text.chars() { + if markers.contains(&char) { + let char_offsets = extracted_markers.entry(char).or_default(); + char_offsets.push(unmarked_text.len()); + } else { + unmarked_text.push(char); + } + } + + (unmarked_text, extracted_markers) +} + +/// Construct a string and a list of ranges within that string using a single +/// string containing embedded range markers, using arbitrary characters as +/// range markers. By using multiple different range markers, you can construct +/// ranges that overlap each other. +/// +/// The returned ranges will be grouped by their range marking characters. +pub fn marked_text_ranges_by( + marked_text: &str, + markers: Vec, +) -> (String, HashMap>>) { + let all_markers = markers.iter().flat_map(|m| m.markers()).collect(); + + let (unmarked_text, mut marker_offsets) = marked_text_offsets_by(marked_text, all_markers); + let range_lookup = markers + .into_iter() + .map(|marker| { + ( + marker.clone(), + match marker { + TextRangeMarker::Empty(empty_marker_char) => marker_offsets + .remove(&empty_marker_char) + .unwrap_or_default() + .into_iter() + .map(|empty_index| empty_index..empty_index) + .collect::>>(), + TextRangeMarker::Range(start_marker, end_marker) => { + let starts = marker_offsets.remove(&start_marker).unwrap_or_default(); + let ends = marker_offsets.remove(&end_marker).unwrap_or_default(); + assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced"); + starts + .into_iter() + .zip(ends) + .map(|(start, end)| { + assert!(end >= start, "marked ranges must be disjoint"); + start..end + }) + .collect::>>() + } + TextRangeMarker::ReverseRange(start_marker, end_marker) => { + let starts = marker_offsets.remove(&start_marker).unwrap_or_default(); + let ends = marker_offsets.remove(&end_marker).unwrap_or_default(); + assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced"); + starts + .into_iter() + .zip(ends) + .map(|(start, end)| { + assert!(end >= start, "marked ranges must be disjoint"); + end..start + }) + .collect::>>() + } + }, + ) + }) + .collect(); + + (unmarked_text, range_lookup) +} + +/// Construct a string and a list of ranges within that string using a single +/// string containing embedded range markers. The characters used to mark the +/// ranges are as follows: +/// +/// 1. To mark a range of text, surround it with the `«` and `»` angle brackets, +/// which can be typed on a US keyboard with the `alt-|` and `alt-shift-|` keys. +/// +/// ```text +/// foo «selected text» bar +/// ``` +/// +/// 2. To mark a single position in the text, use the `ˇ` caron, +/// which can be typed on a US keyboard with the `alt-shift-t` key. +/// +/// ```text +/// the cursors are hereˇ and hereˇ. +/// ``` +/// +/// 3. To mark a range whose direction is meaningful (like a selection), +/// put a caron character beside one of its bounds, on the inside: +/// +/// ```text +/// one «ˇreversed» selection and one «forwardˇ» selection +/// ``` +/// +/// Any • characters in the input string will be replaced with spaces. This makes +/// it easier to test cases with trailing spaces, which tend to get trimmed from the +/// source code. +#[track_caller] +pub fn marked_text_ranges( + marked_text: &str, + ranges_are_directed: bool, +) -> (String, Vec>) { + let mut unmarked_text = String::with_capacity(marked_text.len()); + let mut ranges = Vec::new(); + let mut prev_marked_ix = 0; + let mut current_range_start = None; + let mut current_range_cursor = None; + + let marked_text = marked_text.replace('•', " "); + for (marked_ix, marker) in marked_text.match_indices(&['«', '»', 'ˇ']) { + unmarked_text.push_str(&marked_text[prev_marked_ix..marked_ix]); + let unmarked_len = unmarked_text.len(); + let len = marker.len(); + prev_marked_ix = marked_ix + len; + + match marker { + "ˇ" => { + if current_range_start.is_some() { + if current_range_cursor.is_some() { + panic!("duplicate point marker 'ˇ' at index {marked_ix}"); + } + + current_range_cursor = Some(unmarked_len); + } else { + ranges.push(unmarked_len..unmarked_len); + } + } + "«" => { + if current_range_start.is_some() { + panic!("unexpected range start marker '«' at index {marked_ix}"); + } + current_range_start = Some(unmarked_len); + } + "»" => { + let current_range_start = if let Some(start) = current_range_start.take() { + start + } else { + panic!("unexpected range end marker '»' at index {marked_ix}"); + }; + + let mut reversed = false; + if let Some(current_range_cursor) = current_range_cursor.take() { + if current_range_cursor == current_range_start { + reversed = true; + } else if current_range_cursor != unmarked_len { + panic!("unexpected 'ˇ' marker in the middle of a range"); + } + } else if ranges_are_directed { + panic!("missing 'ˇ' marker to indicate range direction"); + } + + ranges.push(if reversed { + unmarked_len..current_range_start + } else { + current_range_start..unmarked_len + }); + } + _ => unreachable!(), + } + } + + unmarked_text.push_str(&marked_text[prev_marked_ix..]); + (unmarked_text, ranges) +} + +#[track_caller] +pub fn marked_text_offsets(marked_text: &str) -> (String, Vec) { + let (text, ranges) = marked_text_ranges(marked_text, false); + ( + text, + ranges + .into_iter() + .map(|range| { + assert_eq!(range.start, range.end); + range.start + }) + .collect(), + ) +} + +pub fn generate_marked_text( + unmarked_text: &str, + ranges: &[Range], + indicate_cursors: bool, +) -> String { + let mut marked_text = unmarked_text.to_string(); + for range in ranges.iter().rev() { + if indicate_cursors { + match range.start.cmp(&range.end) { + Ordering::Less => { + marked_text.insert_str(range.end, "ˇ»"); + marked_text.insert(range.start, '«'); + } + Ordering::Equal => { + marked_text.insert(range.start, 'ˇ'); + } + Ordering::Greater => { + marked_text.insert(range.start, '»'); + marked_text.insert_str(range.end, "«ˇ"); + } + } + } else { + match range.start.cmp(&range.end) { + Ordering::Equal => { + marked_text.insert(range.start, 'ˇ'); + } + _ => { + marked_text.insert(range.end, '»'); + marked_text.insert(range.start, '«'); + } + } + } + } + marked_text +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub enum TextRangeMarker { + Empty(char), + Range(char, char), + ReverseRange(char, char), +} + +impl TextRangeMarker { + fn markers(&self) -> Vec { + match self { + Self::Empty(m) => vec![*m], + Self::Range(l, r) => vec![*l, *r], + Self::ReverseRange(l, r) => vec![*l, *r], + } + } +} + +impl From for TextRangeMarker { + fn from(marker: char) -> Self { + Self::Empty(marker) + } +} + +impl From<(char, char)> for TextRangeMarker { + fn from((left_marker, right_marker): (char, char)) -> Self { + Self::Range(left_marker, right_marker) + } +} + +#[cfg(test)] +mod tests { + use super::{generate_marked_text, marked_text_ranges}; + + #[allow(clippy::reversed_empty_ranges)] + #[test] + fn test_marked_text() { + let (text, ranges) = marked_text_ranges("one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six", true); + + assert_eq!(text, "one two three four five six"); + assert_eq!(ranges.len(), 4); + assert_eq!(ranges[0], 7..4); + assert_eq!(ranges[1], 8..13); + assert_eq!(ranges[2], 18..14); + assert_eq!(ranges[3], 23..23); + + assert_eq!( + generate_marked_text(&text, &ranges, true), + "one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six" + ); + } +} diff --git a/third_party/gpui_support/gpui_util/src/time.rs b/third_party/gpui_support/gpui_util/src/time.rs new file mode 100644 index 0000000..365de6f --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/time.rs @@ -0,0 +1,41 @@ +use std::time::Duration; + +pub fn duration_alt_display(duration: Duration) -> String { + if duration < Duration::from_secs(60) { + format!("{}s", duration.as_secs()) + } else { + duration_clock_format(duration) + } +} + +fn duration_clock_format(duration: Duration) -> String { + let hours = duration.as_secs() / 3600; + let minutes = (duration.as_secs() % 3600) / 60; + let seconds = duration.as_secs() % 60; + + if hours > 0 { + format!("{hours}:{minutes:02}:{seconds:02}") + } else if minutes > 0 { + format!("{minutes}:{seconds:02}") + } else { + format!("{seconds}") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_duration_to_clock_format() { + use duration_clock_format as f; + assert_eq!("0", f(Duration::from_secs(0))); + assert_eq!("59", f(Duration::from_secs(59))); + assert_eq!("1:00", f(Duration::from_secs(60))); + assert_eq!("10:00", f(Duration::from_secs(600))); + assert_eq!("1:00:00", f(Duration::from_secs(3600))); + assert_eq!("3:02:01", f(Duration::from_secs(3600 * 3 + 60 * 2 + 1))); + assert_eq!("23:59:59", f(Duration::from_secs(3600 * 24 - 1))); + assert_eq!("100:00:00", f(Duration::from_secs(3600 * 100))); + } +} diff --git a/third_party/gpui_support/gpui_util/src/util.rs b/third_party/gpui_support/gpui_util/src/util.rs new file mode 100644 index 0000000..3f1205f --- /dev/null +++ b/third_party/gpui_support/gpui_util/src/util.rs @@ -0,0 +1,1310 @@ +pub mod arc_cow; +pub mod archive; +pub mod command; +pub mod fs; +pub mod markdown; +pub mod paths; +pub mod redact; +pub mod rel_path; +pub mod schemars; +pub mod serde; +pub mod shell; +pub mod shell_env; +pub mod size; +#[cfg(any(test, feature = "test-support"))] +pub mod test; +pub mod time; + +use anyhow::{Context as _, Result}; +use futures::Future; +use itertools::Either; +use paths::PathExt; +use regex::Regex; +use std::path::PathBuf; +use std::sync::{LazyLock, OnceLock}; +use std::{ + borrow::Cow, + cmp::{self, Ordering}, + env, + ops::{AddAssign, Range, RangeInclusive}, + panic::Location, + pin::Pin, + task::{Context, Poll}, + time::Instant, +}; +use unicase::UniCase; + +pub use take_until::*; +#[cfg(any(test, feature = "test-support"))] +pub use util_macros::{line_endings, path, uri}; + +#[macro_export] +macro_rules! debug_panic { + ( $($fmt_arg:tt)* ) => { + if cfg!(debug_assertions) { + panic!( $($fmt_arg)* ); + } else { + let backtrace = std::backtrace::Backtrace::capture(); + log::error!("{}\n{:?}", format_args!($($fmt_arg)*), backtrace); + } + }; +} + +pub fn truncate(s: &str, max_chars: usize) -> &str { + match s.char_indices().nth(max_chars) { + None => s, + Some((idx, _)) => &s[..idx], + } +} + +/// Removes characters from the end of the string if its length is greater than `max_chars` and +/// appends "..." to the string. Returns string unchanged if its length is smaller than max_chars. +pub fn truncate_and_trailoff(s: &str, max_chars: usize) -> String { + debug_assert!(max_chars >= 5); + + // If the string's byte length is <= max_chars, walking the string can be skipped since the + // number of chars is <= the number of bytes. + if s.len() <= max_chars { + return s.to_string(); + } + let truncation_ix = s.char_indices().map(|(i, _)| i).nth(max_chars); + match truncation_ix { + Some(index) => s[..index].to_string() + "…", + _ => s.to_string(), + } +} + +/// Removes characters from the front of the string if its length is greater than `max_chars` and +/// prepends the string with "...". Returns string unchanged if its length is smaller than max_chars. +pub fn truncate_and_remove_front(s: &str, max_chars: usize) -> String { + debug_assert!(max_chars >= 5); + + // If the string's byte length is <= max_chars, walking the string can be skipped since the + // number of chars is <= the number of bytes. + if s.len() <= max_chars { + return s.to_string(); + } + let suffix_char_length = max_chars.saturating_sub(1); + let truncation_ix = s + .char_indices() + .map(|(i, _)| i) + .nth_back(suffix_char_length); + match truncation_ix { + Some(index) if index > 0 => "…".to_string() + &s[index..], + _ => s.to_string(), + } +} + +/// Takes only `max_lines` from the string and, if there were more than `max_lines-1`, appends a +/// a newline and "..." to the string, so that `max_lines` are returned. +/// Returns string unchanged if its length is smaller than max_lines. +pub fn truncate_lines_and_trailoff(s: &str, max_lines: usize) -> String { + let mut lines = s.lines().take(max_lines).collect::>(); + if lines.len() > max_lines - 1 { + lines.pop(); + lines.join("\n") + "\n…" + } else { + lines.join("\n") + } +} + +/// Truncates the string at a character boundary, such that the result is less than `max_bytes` in +/// length. +pub fn truncate_to_byte_limit(s: &str, max_bytes: usize) -> &str { + if s.len() < max_bytes { + return s; + } + + for i in (0..max_bytes).rev() { + if s.is_char_boundary(i) { + return &s[..i]; + } + } + + "" +} + +/// Takes a prefix of complete lines which fit within the byte limit. If the first line is longer +/// than the limit, truncates at a character boundary. +pub fn truncate_lines_to_byte_limit(s: &str, max_bytes: usize) -> &str { + if s.len() < max_bytes { + return s; + } + + for i in (0..max_bytes).rev() { + if s.is_char_boundary(i) && s.as_bytes()[i] == b'\n' { + // Since the i-th character is \n, valid to slice at i + 1. + return &s[..i + 1]; + } + } + + truncate_to_byte_limit(s, max_bytes) +} + +#[test] +fn test_truncate_lines_to_byte_limit() { + let text = "Line 1\nLine 2\nLine 3\nLine 4"; + + // Limit that includes all lines + assert_eq!(truncate_lines_to_byte_limit(text, 100), text); + + // Exactly the first line + assert_eq!(truncate_lines_to_byte_limit(text, 7), "Line 1\n"); + + // Limit between lines + assert_eq!(truncate_lines_to_byte_limit(text, 13), "Line 1\n"); + assert_eq!(truncate_lines_to_byte_limit(text, 20), "Line 1\nLine 2\n"); + + // Limit before first newline + assert_eq!(truncate_lines_to_byte_limit(text, 6), "Line "); + + // Test with non-ASCII characters + let text_utf8 = "Line 1\nLíne 2\nLine 3"; + assert_eq!( + truncate_lines_to_byte_limit(text_utf8, 15), + "Line 1\nLíne 2\n" + ); +} + +pub fn post_inc + AddAssign + Copy>(value: &mut T) -> T { + let prev = *value; + *value += T::from(1); + prev +} + +/// Extend a sorted vector with a sorted sequence of items, maintaining the vector's sort order and +/// enforcing a maximum length. This also de-duplicates items. Sort the items according to the given callback. Before calling this, +/// both `vec` and `new_items` should already be sorted according to the `cmp` comparator. +pub fn extend_sorted(vec: &mut Vec, new_items: I, limit: usize, mut cmp: F) +where + I: IntoIterator, + F: FnMut(&T, &T) -> Ordering, +{ + let mut start_index = 0; + for new_item in new_items { + if let Err(i) = vec[start_index..].binary_search_by(|m| cmp(m, &new_item)) { + let index = start_index + i; + if vec.len() < limit { + vec.insert(index, new_item); + } else if index < vec.len() { + vec.pop(); + vec.insert(index, new_item); + } + start_index = index; + } + } +} + +pub fn truncate_to_bottom_n_sorted_by(items: &mut Vec, limit: usize, compare: &F) +where + F: Fn(&T, &T) -> Ordering, +{ + if limit == 0 { + items.truncate(0); + } + if items.len() <= limit { + items.sort_by(compare); + return; + } + // When limit is near to items.len() it may be more efficient to sort the whole list and + // truncate, rather than always doing selection first as is done below. It's hard to analyze + // where the threshold for this should be since the quickselect style algorithm used by + // `select_nth_unstable_by` makes the prefix partially sorted, and so its work is not wasted - + // the expected number of comparisons needed by `sort_by` is less than it is for some arbitrary + // unsorted input. + items.select_nth_unstable_by(limit, compare); + items.truncate(limit); + items.sort_by(compare); +} + +/// Prevents execution of the application with root privileges on Unix systems. +/// +/// This function checks if the current process is running with root privileges +/// and terminates the program with an error message unless explicitly allowed via the +/// `ZED_ALLOW_ROOT` environment variable. +#[cfg(unix)] +pub fn prevent_root_execution() { + let is_root = nix::unistd::geteuid().is_root(); + let allow_root = std::env::var("ZED_ALLOW_ROOT").is_ok_and(|val| val == "true"); + + if is_root && !allow_root { + eprintln!( + "\ +Error: Running Zed as root or via sudo is unsupported. + Doing so (even once) may subtly break things for all subsequent non-root usage of Zed. + It is untested and not recommended, don't complain when things break. + If you wish to proceed anyways, set `ZED_ALLOW_ROOT=true` in your environment." + ); + std::process::exit(1); + } +} + +#[cfg(unix)] +fn load_shell_from_passwd() -> Result<()> { + let buflen = match unsafe { libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) } { + n if n < 0 => 1024, + n => n as usize, + }; + let mut buffer = Vec::with_capacity(buflen); + + let mut pwd: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); + let mut result: *mut libc::passwd = std::ptr::null_mut(); + + let uid = unsafe { libc::getuid() }; + let status = unsafe { + libc::getpwuid_r( + uid, + pwd.as_mut_ptr(), + buffer.as_mut_ptr() as *mut libc::c_char, + buflen, + &mut result, + ) + }; + anyhow::ensure!(!result.is_null(), "passwd entry for uid {} not found", uid); + + // SAFETY: If `getpwuid_r` doesn't error, we have the entry here. + let entry = unsafe { pwd.assume_init() }; + + anyhow::ensure!( + status == 0, + "call to getpwuid_r failed. uid: {}, status: {}", + uid, + status + ); + anyhow::ensure!( + entry.pw_uid == uid, + "passwd entry has different uid ({}) than getuid ({}) returned", + entry.pw_uid, + uid, + ); + + let shell = unsafe { std::ffi::CStr::from_ptr(entry.pw_shell).to_str().unwrap() }; + let should_set_shell = env::var("SHELL").map_or(true, |shell_env| { + shell_env != shell && !std::path::Path::new(&shell_env).exists() + }); + + if should_set_shell { + log::info!( + "updating SHELL environment variable to value from passwd entry: {:?}", + shell, + ); + unsafe { env::set_var("SHELL", shell) }; + } + + Ok(()) +} + +/// Returns a shell escaped path for the current zed executable +pub fn get_shell_safe_zed_path() -> anyhow::Result { + let zed_path = + std::env::current_exe().context("Failed to determine current zed executable path.")?; + + zed_path + .try_shell_safe() + .context("Failed to shell-escape Zed executable path.") +} + +/// Returns a path for the zed cli executable, this function +/// should be called from the zed executable, not zed-cli. +pub fn get_zed_cli_path() -> Result { + let zed_path = + std::env::current_exe().context("Failed to determine current zed executable path.")?; + let parent = zed_path + .parent() + .context("Failed to determine parent directory of zed executable path.")?; + + let possible_locations: &[&str] = if cfg!(target_os = "macos") { + // On macOS, the zed executable and zed-cli are inside the app bundle, + // so here ./cli is for both installed and development builds. + &["./cli"] + } else if cfg!(target_os = "windows") { + // bin/zed.exe is for installed builds, ./cli.exe is for development builds. + &["bin/zed.exe", "./cli.exe"] + } else if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") { + // bin is the standard, ./cli is for the target directory in development builds. + &["../bin/zed", "./cli"] + } else { + anyhow::bail!("unsupported platform for determining zed-cli path"); + }; + + possible_locations + .iter() + .find_map(|p| { + parent + .join(p) + .canonicalize() + .ok() + .filter(|p| p != &zed_path) + }) + .with_context(|| { + format!( + "could not find zed-cli from any of: {}", + possible_locations.join(", ") + ) + }) +} + +#[cfg(unix)] +pub async fn load_login_shell_environment() -> Result<()> { + load_shell_from_passwd().log_err(); + + // If possible, we want to `cd` in the user's `$HOME` to trigger programs + // such as direnv, asdf, mise, ... to adjust the PATH. These tools often hook + // into shell's `cd` command (and hooks) to manipulate env. + // We do this so that we get the env a user would have when spawning a shell + // in home directory. + for (name, value) in shell_env::capture(get_system_shell(), &[], paths::home_dir()).await? { + unsafe { env::set_var(&name, &value) }; + } + + log::info!( + "set environment variables from shell:{}, path:{}", + std::env::var("SHELL").unwrap_or_default(), + std::env::var("PATH").unwrap_or_default(), + ); + + Ok(()) +} + +/// Configures the process to start a new session, to prevent interactive shells from taking control +/// of the terminal. +/// +/// For more details: +pub fn set_pre_exec_to_start_new_session( + command: &mut std::process::Command, +) -> &mut std::process::Command { + // safety: code in pre_exec should be signal safe. + // https://man7.org/linux/man-pages/man7/signal-safety.7.html + #[cfg(not(target_os = "windows"))] + unsafe { + use std::os::unix::process::CommandExt; + command.pre_exec(|| { + libc::setsid(); + Ok(()) + }); + }; + command +} + +pub fn merge_json_lenient_value_into( + source: serde_json_lenient::Value, + target: &mut serde_json_lenient::Value, +) { + match (source, target) { + (serde_json_lenient::Value::Object(source), serde_json_lenient::Value::Object(target)) => { + for (key, value) in source { + if let Some(target) = target.get_mut(&key) { + merge_json_lenient_value_into(value, target); + } else { + target.insert(key, value); + } + } + } + + (serde_json_lenient::Value::Array(source), serde_json_lenient::Value::Array(target)) => { + for value in source { + target.push(value); + } + } + + (source, target) => *target = source, + } +} + +pub fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { + use serde_json::Value; + + match (source, target) { + (Value::Object(source), Value::Object(target)) => { + for (key, value) in source { + if let Some(target) = target.get_mut(&key) { + merge_json_value_into(value, target); + } else { + target.insert(key, value); + } + } + } + + (Value::Array(source), Value::Array(target)) => { + for value in source { + target.push(value); + } + } + + (source, target) => *target = source, + } +} + +pub fn merge_non_null_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { + use serde_json::Value; + if let Value::Object(source_object) = source { + let target_object = if let Value::Object(target) = target { + target + } else { + *target = Value::Object(Default::default()); + target.as_object_mut().unwrap() + }; + for (key, value) in source_object { + if let Some(target) = target_object.get_mut(&key) { + merge_non_null_json_value_into(value, target); + } else if !value.is_null() { + target_object.insert(key, value); + } + } + } else if !source.is_null() { + *target = source + } +} + +pub fn measure(label: &str, f: impl FnOnce() -> R) -> R { + static ZED_MEASUREMENTS: OnceLock = OnceLock::new(); + let zed_measurements = ZED_MEASUREMENTS.get_or_init(|| { + env::var("ZED_MEASUREMENTS") + .map(|measurements| measurements == "1" || measurements == "true") + .unwrap_or(false) + }); + + if *zed_measurements { + let start = Instant::now(); + let result = f(); + let elapsed = start.elapsed(); + eprintln!("{}: {:?}", label, elapsed); + result + } else { + f() + } +} + +pub fn expanded_and_wrapped_usize_range( + range: Range, + additional_before: usize, + additional_after: usize, + wrap_length: usize, +) -> impl Iterator { + let start_wraps = range.start < additional_before; + let end_wraps = wrap_length < range.end + additional_after; + if start_wraps && end_wraps { + Either::Left(0..wrap_length) + } else if start_wraps { + let wrapped_start = (range.start + wrap_length).saturating_sub(additional_before); + if wrapped_start <= range.end { + Either::Left(0..wrap_length) + } else { + Either::Right((0..range.end + additional_after).chain(wrapped_start..wrap_length)) + } + } else if end_wraps { + let wrapped_end = range.end + additional_after - wrap_length; + if range.start <= wrapped_end { + Either::Left(0..wrap_length) + } else { + Either::Right((0..wrapped_end).chain(range.start - additional_before..wrap_length)) + } + } else { + Either::Left((range.start - additional_before)..(range.end + additional_after)) + } +} + +/// Yields `[i, i + 1, i - 1, i + 2, ..]`, each modulo `wrap_length` and bounded by +/// `additional_before` and `additional_after`. If the wrapping causes overlap, duplicates are not +/// emitted. If wrap_length is 0, nothing is yielded. +pub fn wrapped_usize_outward_from( + start: usize, + additional_before: usize, + additional_after: usize, + wrap_length: usize, +) -> impl Iterator { + let mut count = 0; + let mut after_offset = 1; + let mut before_offset = 1; + + std::iter::from_fn(move || { + count += 1; + if count > wrap_length { + None + } else if count == 1 { + Some(start % wrap_length) + } else if after_offset <= additional_after && after_offset <= before_offset { + let value = (start + after_offset) % wrap_length; + after_offset += 1; + Some(value) + } else if before_offset <= additional_before { + let value = (start + wrap_length - before_offset) % wrap_length; + before_offset += 1; + Some(value) + } else if after_offset <= additional_after { + let value = (start + after_offset) % wrap_length; + after_offset += 1; + Some(value) + } else { + None + } + }) +} + +pub trait ResultExt { + type Ok; + + fn log_err(self) -> Option; + /// Assert that this result should never be an error in development or tests. + fn debug_assert_ok(self, reason: &str) -> Self; + fn warn_on_err(self) -> Option; + fn log_with_level(self, level: log::Level) -> Option; + fn anyhow(self) -> anyhow::Result + where + E: Into; +} + +impl ResultExt for Result +where + E: std::fmt::Debug, +{ + type Ok = T; + + #[track_caller] + fn log_err(self) -> Option { + self.log_with_level(log::Level::Error) + } + + #[track_caller] + fn debug_assert_ok(self, reason: &str) -> Self { + if let Err(error) = &self { + debug_panic!("{reason} - {error:?}"); + } + self + } + + #[track_caller] + fn warn_on_err(self) -> Option { + self.log_with_level(log::Level::Warn) + } + + #[track_caller] + fn log_with_level(self, level: log::Level) -> Option { + match self { + Ok(value) => Some(value), + Err(error) => { + log_error_with_caller(*Location::caller(), error, level); + None + } + } + } + + fn anyhow(self) -> anyhow::Result + where + E: Into, + { + self.map_err(Into::into) + } +} + +fn log_error_with_caller(caller: core::panic::Location<'_>, error: E, level: log::Level) +where + E: std::fmt::Debug, +{ + #[cfg(not(target_os = "windows"))] + let file = caller.file(); + #[cfg(target_os = "windows")] + let file = caller.file().replace('\\', "/"); + // In this codebase all crates reside in a `crates` directory, + // so discard the prefix up to that segment to find the crate name + let target = file + .split_once("crates/") + .and_then(|(_, s)| s.split_once("/src/")); + + let module_path = target.map(|(krate, module)| { + krate.to_owned() + "::" + &module.trim_end_matches(".rs").replace('/', "::") + }); + log::logger().log( + &log::Record::builder() + .target(target.map_or("", |(krate, _)| krate)) + .module_path(module_path.as_deref()) + .args(format_args!("{:?}", error)) + .file(Some(caller.file())) + .line(Some(caller.line())) + .level(level) + .build(), + ); +} + +pub fn log_err(error: &E) { + log_error_with_caller(*Location::caller(), error, log::Level::Warn); +} + +pub trait TryFutureExt { + fn log_err(self) -> LogErrorFuture + where + Self: Sized; + + fn log_tracked_err(self, location: core::panic::Location<'static>) -> LogErrorFuture + where + Self: Sized; + + fn warn_on_err(self) -> LogErrorFuture + where + Self: Sized; + fn unwrap(self) -> UnwrapFuture + where + Self: Sized; +} + +impl TryFutureExt for F +where + F: Future>, + E: std::fmt::Debug, +{ + #[track_caller] + fn log_err(self) -> LogErrorFuture + where + Self: Sized, + { + let location = Location::caller(); + LogErrorFuture(self, log::Level::Error, *location) + } + + fn log_tracked_err(self, location: core::panic::Location<'static>) -> LogErrorFuture + where + Self: Sized, + { + LogErrorFuture(self, log::Level::Error, location) + } + + #[track_caller] + fn warn_on_err(self) -> LogErrorFuture + where + Self: Sized, + { + let location = Location::caller(); + LogErrorFuture(self, log::Level::Warn, *location) + } + + fn unwrap(self) -> UnwrapFuture + where + Self: Sized, + { + UnwrapFuture(self) + } +} + +#[must_use] +pub struct LogErrorFuture(F, log::Level, core::panic::Location<'static>); + +impl Future for LogErrorFuture +where + F: Future>, + E: std::fmt::Debug, +{ + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let level = self.1; + let location = self.2; + let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }; + match inner.poll(cx) { + Poll::Ready(output) => Poll::Ready(match output { + Ok(output) => Some(output), + Err(error) => { + log_error_with_caller(location, error, level); + None + } + }), + Poll::Pending => Poll::Pending, + } + } +} + +pub struct UnwrapFuture(F); + +impl Future for UnwrapFuture +where + F: Future>, + E: std::fmt::Debug, +{ + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }; + match inner.poll(cx) { + Poll::Ready(result) => Poll::Ready(result.unwrap()), + Poll::Pending => Poll::Pending, + } + } +} + +pub struct Deferred(Option); + +impl Deferred { + /// Drop without running the deferred function. + pub fn abort(mut self) { + self.0.take(); + } +} + +impl Drop for Deferred { + fn drop(&mut self) { + if let Some(f) = self.0.take() { + f() + } + } +} + +/// Run the given function when the returned value is dropped (unless it's cancelled). +#[must_use] +pub fn defer(f: F) -> Deferred { + Deferred(Some(f)) +} + +#[cfg(any(test, feature = "test-support"))] +mod rng { + use rand::prelude::*; + + pub struct RandomCharIter { + rng: T, + simple_text: bool, + } + + impl RandomCharIter { + pub fn new(rng: T) -> Self { + Self { + rng, + simple_text: std::env::var("SIMPLE_TEXT").is_ok_and(|v| !v.is_empty()), + } + } + + pub fn with_simple_text(mut self) -> Self { + self.simple_text = true; + self + } + } + + impl Iterator for RandomCharIter { + type Item = char; + + fn next(&mut self) -> Option { + if self.simple_text { + return if self.rng.random_range(0..100) < 5 { + Some('\n') + } else { + Some(self.rng.random_range(b'a'..b'z' + 1).into()) + }; + } + + match self.rng.random_range(0..100) { + // whitespace + 0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.rng).copied(), + // two-byte greek letters + 20..=32 => char::from_u32(self.rng.random_range(('α' as u32)..('ω' as u32 + 1))), + // // three-byte characters + 33..=45 => ['✋', '✅', '❌', '❎', '⭐'] + .choose(&mut self.rng) + .copied(), + // // four-byte characters + 46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.rng).copied(), + // ascii letters + _ => Some(self.rng.random_range(b'a'..b'z' + 1).into()), + } + } + } +} +#[cfg(any(test, feature = "test-support"))] +pub use rng::RandomCharIter; + +/// Get an embedded file as a string. +pub fn asset_str(path: &str) -> Cow<'static, str> { + match A::get(path).expect(path).data { + Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8(bytes).unwrap()), + Cow::Owned(bytes) => Cow::Owned(String::from_utf8(bytes).unwrap()), + } +} + +/// Expands to an immediately-invoked function expression. Good for using the ? operator +/// in functions which do not return an Option or Result. +/// +/// Accepts a normal block, an async block, or an async move block. +#[macro_export] +macro_rules! maybe { + ($block:block) => { + (|| $block)() + }; + (async $block:block) => { + (async || $block)() + }; + (async move $block:block) => { + (async move || $block)() + }; +} + +pub trait RangeExt { + fn sorted(&self) -> Self; + fn to_inclusive(&self) -> RangeInclusive; + fn overlaps(&self, other: &Range) -> bool; + fn contains_inclusive(&self, other: &Range) -> bool; +} + +impl RangeExt for Range { + fn sorted(&self) -> Self { + cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone() + } + + fn to_inclusive(&self) -> RangeInclusive { + self.start.clone()..=self.end.clone() + } + + fn overlaps(&self, other: &Range) -> bool { + self.start < other.end && other.start < self.end + } + + fn contains_inclusive(&self, other: &Range) -> bool { + self.start <= other.start && other.end <= self.end + } +} + +impl RangeExt for RangeInclusive { + fn sorted(&self) -> Self { + cmp::min(self.start(), self.end()).clone()..=cmp::max(self.start(), self.end()).clone() + } + + fn to_inclusive(&self) -> RangeInclusive { + self.clone() + } + + fn overlaps(&self, other: &Range) -> bool { + self.start() < &other.end && &other.start <= self.end() + } + + fn contains_inclusive(&self, other: &Range) -> bool { + self.start() <= &other.start && &other.end <= self.end() + } +} + +/// A way to sort strings with starting numbers numerically first, falling back to alphanumeric one, +/// case-insensitive. +/// +/// This is useful for turning regular alphanumerically sorted sequences as `1-abc, 10, 11-def, .., 2, 21-abc` +/// into `1-abc, 2, 10, 11-def, .., 21-abc` +#[derive(Debug, PartialEq, Eq)] +pub struct NumericPrefixWithSuffix<'a>(Option, &'a str); + +impl<'a> NumericPrefixWithSuffix<'a> { + pub fn from_numeric_prefixed_str(str: &'a str) -> Self { + let i = str.chars().take_while(|c| c.is_ascii_digit()).count(); + let (prefix, remainder) = str.split_at(i); + + let prefix = prefix.parse().ok(); + Self(prefix, remainder) + } +} + +/// When dealing with equality, we need to consider the case of the strings to achieve strict equality +/// to handle cases like "a" < "A" instead of "a" == "A". +impl Ord for NumericPrefixWithSuffix<'_> { + fn cmp(&self, other: &Self) -> Ordering { + match (self.0, other.0) { + (None, None) => UniCase::new(self.1) + .cmp(&UniCase::new(other.1)) + .then_with(|| self.1.cmp(other.1).reverse()), + (None, Some(_)) => Ordering::Greater, + (Some(_), None) => Ordering::Less, + (Some(a), Some(b)) => a.cmp(&b).then_with(|| { + UniCase::new(self.1) + .cmp(&UniCase::new(other.1)) + .then_with(|| self.1.cmp(other.1).reverse()) + }), + } + } +} + +impl PartialOrd for NumericPrefixWithSuffix<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Capitalizes the first character of a string. +/// +/// This function takes a string slice as input and returns a new `String` with the first character +/// capitalized. +/// +/// # Examples +/// +/// ``` +/// use util::capitalize; +/// +/// assert_eq!(capitalize("hello"), "Hello"); +/// assert_eq!(capitalize("WORLD"), "WORLD"); +/// assert_eq!(capitalize(""), ""); +/// ``` +pub fn capitalize(str: &str) -> String { + let mut chars = str.chars(); + match chars.next() { + None => String::new(), + Some(first_char) => first_char.to_uppercase().collect::() + chars.as_str(), + } +} + +fn emoji_regex() -> &'static Regex { + static EMOJI_REGEX: LazyLock = + LazyLock::new(|| Regex::new("(\\p{Emoji}|\u{200D})").unwrap()); + &EMOJI_REGEX +} + +/// Returns true if the given string consists of emojis only. +/// E.g. "👨‍👩‍👧‍👧👋" will return true, but "👋!" will return false. +pub fn word_consists_of_emojis(s: &str) -> bool { + let mut prev_end = 0; + for capture in emoji_regex().find_iter(s) { + if capture.start() != prev_end { + return false; + } + prev_end = capture.end(); + } + prev_end == s.len() +} + +/// Similar to `str::split`, but also provides byte-offset ranges of the results. Unlike +/// `str::split`, this is not generic on pattern types and does not return an `Iterator`. +pub fn split_str_with_ranges(s: &str, pat: impl Fn(char) -> bool) -> Vec<(Range, &str)> { + let mut result = Vec::new(); + let mut start = 0; + + for (i, ch) in s.char_indices() { + if pat(ch) { + if i > start { + result.push((start..i, &s[start..i])); + } + start = i + ch.len_utf8(); + } + } + + if s.len() > start { + result.push((start..s.len(), &s[start..s.len()])); + } + + result +} + +pub fn default() -> D { + Default::default() +} + +pub use self::shell::{ + get_default_system_shell, get_default_system_shell_preferring_bash, get_system_shell, +}; + +#[derive(Debug)] +pub enum ConnectionResult { + Timeout, + ConnectionReset, + Result(anyhow::Result), +} + +impl ConnectionResult { + pub fn into_response(self) -> anyhow::Result { + match self { + ConnectionResult::Timeout => anyhow::bail!("Request timed out"), + ConnectionResult::ConnectionReset => anyhow::bail!("Server reset the connection"), + ConnectionResult::Result(r) => r, + } + } +} + +impl From> for ConnectionResult { + fn from(result: anyhow::Result) -> Self { + ConnectionResult::Result(result) + } +} + +#[track_caller] +pub fn some_or_debug_panic(option: Option) -> Option { + #[cfg(debug_assertions)] + if option.is_none() { + panic!("Unexpected None"); + } + option +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_extend_sorted() { + let mut vec = vec![]; + + extend_sorted(&mut vec, vec![21, 17, 13, 8, 1, 0], 5, |a, b| b.cmp(a)); + assert_eq!(vec, &[21, 17, 13, 8, 1]); + + extend_sorted(&mut vec, vec![101, 19, 17, 8, 2], 8, |a, b| b.cmp(a)); + assert_eq!(vec, &[101, 21, 19, 17, 13, 8, 2, 1]); + + extend_sorted(&mut vec, vec![1000, 19, 17, 9, 5], 8, |a, b| b.cmp(a)); + assert_eq!(vec, &[1000, 101, 21, 19, 17, 13, 9, 8]); + } + + #[test] + fn test_truncate_to_bottom_n_sorted_by() { + let mut vec: Vec = vec![5, 2, 3, 4, 1]; + truncate_to_bottom_n_sorted_by(&mut vec, 10, &u32::cmp); + assert_eq!(vec, &[1, 2, 3, 4, 5]); + + vec = vec![5, 2, 3, 4, 1]; + truncate_to_bottom_n_sorted_by(&mut vec, 5, &u32::cmp); + assert_eq!(vec, &[1, 2, 3, 4, 5]); + + vec = vec![5, 2, 3, 4, 1]; + truncate_to_bottom_n_sorted_by(&mut vec, 4, &u32::cmp); + assert_eq!(vec, &[1, 2, 3, 4]); + + vec = vec![5, 2, 3, 4, 1]; + truncate_to_bottom_n_sorted_by(&mut vec, 1, &u32::cmp); + assert_eq!(vec, &[1]); + + vec = vec![5, 2, 3, 4, 1]; + truncate_to_bottom_n_sorted_by(&mut vec, 0, &u32::cmp); + assert!(vec.is_empty()); + } + + #[test] + fn test_iife() { + fn option_returning_function() -> Option<()> { + None + } + + let foo = maybe!({ + option_returning_function()?; + Some(()) + }); + + assert_eq!(foo, None); + } + + #[test] + fn test_truncate_and_trailoff() { + assert_eq!(truncate_and_trailoff("", 5), ""); + assert_eq!(truncate_and_trailoff("aaaaaa", 7), "aaaaaa"); + assert_eq!(truncate_and_trailoff("aaaaaa", 6), "aaaaaa"); + assert_eq!(truncate_and_trailoff("aaaaaa", 5), "aaaaa…"); + assert_eq!(truncate_and_trailoff("èèèèèè", 7), "èèèèèè"); + assert_eq!(truncate_and_trailoff("èèèèèè", 6), "èèèèèè"); + assert_eq!(truncate_and_trailoff("èèèèèè", 5), "èèèèè…"); + } + + #[test] + fn test_truncate_and_remove_front() { + assert_eq!(truncate_and_remove_front("", 5), ""); + assert_eq!(truncate_and_remove_front("aaaaaa", 7), "aaaaaa"); + assert_eq!(truncate_and_remove_front("aaaaaa", 6), "aaaaaa"); + assert_eq!(truncate_and_remove_front("aaaaaa", 5), "…aaaaa"); + assert_eq!(truncate_and_remove_front("èèèèèè", 7), "èèèèèè"); + assert_eq!(truncate_and_remove_front("èèèèèè", 6), "èèèèèè"); + assert_eq!(truncate_and_remove_front("èèèèèè", 5), "…èèèèè"); + } + + #[test] + fn test_numeric_prefix_str_method() { + let target = "1a"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(1), "a") + ); + + let target = "12ab"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(12), "ab") + ); + + let target = "12_ab"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(12), "_ab") + ); + + let target = "1_2ab"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(1), "_2ab") + ); + + let target = "1.2"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(1), ".2") + ); + + let target = "1.2_a"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(1), ".2_a") + ); + + let target = "12.2_a"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(12), ".2_a") + ); + + let target = "12a.2_a"; + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(target), + NumericPrefixWithSuffix(Some(12), "a.2_a") + ); + } + + #[test] + fn test_numeric_prefix_with_suffix() { + let mut sorted = vec!["1-abc", "10", "11def", "2", "21-abc"]; + sorted.sort_by_key(|s| NumericPrefixWithSuffix::from_numeric_prefixed_str(s)); + assert_eq!(sorted, ["1-abc", "2", "10", "11def", "21-abc"]); + + for numeric_prefix_less in ["numeric_prefix_less", "aaa", "~™£"] { + assert_eq!( + NumericPrefixWithSuffix::from_numeric_prefixed_str(numeric_prefix_less), + NumericPrefixWithSuffix(None, numeric_prefix_less), + "String without numeric prefix `{numeric_prefix_less}` should not be converted into NumericPrefixWithSuffix" + ) + } + } + + #[test] + fn test_word_consists_of_emojis() { + let words_to_test = vec![ + ("👨‍👩‍👧‍👧👋🥒", true), + ("👋", true), + ("!👋", false), + ("👋!", false), + ("👋 ", false), + (" 👋", false), + ("Test", false), + ]; + + for (text, expected_result) in words_to_test { + assert_eq!(word_consists_of_emojis(text), expected_result); + } + } + + #[test] + fn test_truncate_lines_and_trailoff() { + let text = r#"Line 1 +Line 2 +Line 3"#; + + assert_eq!( + truncate_lines_and_trailoff(text, 2), + r#"Line 1 +…"# + ); + + assert_eq!( + truncate_lines_and_trailoff(text, 3), + r#"Line 1 +Line 2 +…"# + ); + + assert_eq!( + truncate_lines_and_trailoff(text, 4), + r#"Line 1 +Line 2 +Line 3"# + ); + } + + #[test] + fn test_expanded_and_wrapped_usize_range() { + // Neither wrap + assert_eq!( + expanded_and_wrapped_usize_range(2..4, 1, 1, 8).collect::>(), + (1..5).collect::>() + ); + // Start wraps + assert_eq!( + expanded_and_wrapped_usize_range(2..4, 3, 1, 8).collect::>(), + ((0..5).chain(7..8)).collect::>() + ); + // Start wraps all the way around + assert_eq!( + expanded_and_wrapped_usize_range(2..4, 5, 1, 8).collect::>(), + (0..8).collect::>() + ); + // Start wraps all the way around and past 0 + assert_eq!( + expanded_and_wrapped_usize_range(2..4, 10, 1, 8).collect::>(), + (0..8).collect::>() + ); + // End wraps + assert_eq!( + expanded_and_wrapped_usize_range(3..5, 1, 4, 8).collect::>(), + (0..1).chain(2..8).collect::>() + ); + // End wraps all the way around + assert_eq!( + expanded_and_wrapped_usize_range(3..5, 1, 5, 8).collect::>(), + (0..8).collect::>() + ); + // End wraps all the way around and past the end + assert_eq!( + expanded_and_wrapped_usize_range(3..5, 1, 10, 8).collect::>(), + (0..8).collect::>() + ); + // Both start and end wrap + assert_eq!( + expanded_and_wrapped_usize_range(3..5, 4, 4, 8).collect::>(), + (0..8).collect::>() + ); + } + + #[test] + fn test_wrapped_usize_outward_from() { + // No wrapping + assert_eq!( + wrapped_usize_outward_from(4, 2, 2, 10).collect::>(), + vec![4, 5, 3, 6, 2] + ); + // Wrapping at end + assert_eq!( + wrapped_usize_outward_from(8, 2, 3, 10).collect::>(), + vec![8, 9, 7, 0, 6, 1] + ); + // Wrapping at start + assert_eq!( + wrapped_usize_outward_from(1, 3, 2, 10).collect::>(), + vec![1, 2, 0, 3, 9, 8] + ); + // All values wrap around + assert_eq!( + wrapped_usize_outward_from(5, 10, 10, 8).collect::>(), + vec![5, 6, 4, 7, 3, 0, 2, 1] + ); + // None before / after + assert_eq!( + wrapped_usize_outward_from(3, 0, 0, 8).collect::>(), + vec![3] + ); + // Starting point already wrapped + assert_eq!( + wrapped_usize_outward_from(15, 2, 2, 10).collect::>(), + vec![5, 6, 4, 7, 3] + ); + // wrap_length of 0 + assert_eq!( + wrapped_usize_outward_from(4, 2, 2, 0).collect::>(), + Vec::::new() + ); + } + + #[test] + fn test_split_with_ranges() { + let input = "hi"; + let result = split_str_with_ranges(input, |c| c == ' '); + + assert_eq!(result.len(), 1); + assert_eq!(result[0], (0..2, "hi")); + + let input = "héllo🦀world"; + let result = split_str_with_ranges(input, |c| c == '🦀'); + + assert_eq!(result.len(), 2); + assert_eq!(result[0], (0..6, "héllo")); // 'é' is 2 bytes + assert_eq!(result[1], (10..15, "world")); // '🦀' is 4 bytes + } +} diff --git a/third_party/gpui_support/zed-sum-tree/Cargo.toml b/third_party/gpui_support/zed-sum-tree/Cargo.toml new file mode 100644 index 0000000..9076a79 --- /dev/null +++ b/third_party/gpui_support/zed-sum-tree/Cargo.toml @@ -0,0 +1,69 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# Local copy keeps the registry manifest shape and narrows log features so +# GPUI and Servo can share the same workspace dependency graph. + +[package] +edition = "2024" +name = "zed-sum-tree" +version = "0.2.0" +build = false +publish = true +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A sum tree data structure, a concurrency-friendly B-tree" +readme = false +license = "Apache-2.0" +resolver = "2" + +[lib] +name = "zed_sum_tree" +path = "src/sum_tree.rs" +doctest = false + +[dependencies.arrayvec] +version = "0.7.1" + +[dependencies.log] +version = "0.4.16" + +[dependencies.rayon] +version = "1.8" + +[dependencies.workspace-hack] +version = "0.1.0" + +[dev-dependencies.ctor] +version = "0.4.0" + +[dev-dependencies.rand] +version = "0.9" + +[lints.clippy] +dbg_macro = "deny" +declare_interior_mutable_const = "deny" +disallowed_methods = "deny" +large_enum_variant = "allow" +let_underscore_future = "allow" +nonminimal_bool = "allow" +redundant_clone = "deny" +single_range_in_vec_init = "allow" +todo = "deny" +too_many_arguments = "allow" +type_complexity = "allow" + +[lints.clippy.style] +level = "allow" +priority = -1 + +[lints.rust.unexpected_cfgs] +level = "allow" +priority = 0 diff --git a/third_party/gpui_support/zed-sum-tree/LICENSE-APACHE b/third_party/gpui_support/zed-sum-tree/LICENSE-APACHE new file mode 100644 index 0000000..461a0fe --- /dev/null +++ b/third_party/gpui_support/zed-sum-tree/LICENSE-APACHE @@ -0,0 +1,222 @@ +Copyright 2022 - 2025 Zed Industries, Inc. + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + + 1. Definitions. + + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + END OF TERMS AND CONDITIONS diff --git a/third_party/gpui_support/zed-sum-tree/src/cursor.rs b/third_party/gpui_support/zed-sum-tree/src/cursor.rs new file mode 100644 index 0000000..6df1d3d --- /dev/null +++ b/third_party/gpui_support/zed-sum-tree/src/cursor.rs @@ -0,0 +1,817 @@ +use super::*; +use arrayvec::ArrayVec; +use std::{cmp::Ordering, mem, sync::Arc}; + +#[derive(Clone)] +struct StackEntry<'a, T: Item, D> { + tree: &'a SumTree, + index: u32, + position: D, +} + +impl<'a, T: Item, D> StackEntry<'a, T, D> { + #[inline] + fn index(&self) -> usize { + self.index as usize + } +} + +impl fmt::Debug for StackEntry<'_, T, D> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StackEntry") + .field("index", &self.index) + .field("position", &self.position) + .finish() + } +} + +#[derive(Clone)] +pub struct Cursor<'a, 'b, T: Item, D> { + tree: &'a SumTree, + stack: ArrayVec, 16>, + position: D, + did_seek: bool, + at_end: bool, + cx: ::Context<'b>, +} + +impl fmt::Debug for Cursor<'_, '_, T, D> +where + T::Summary: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Cursor") + .field("tree", &self.tree) + .field("stack", &self.stack) + .field("position", &self.position) + .field("did_seek", &self.did_seek) + .field("at_end", &self.at_end) + .finish() + } +} + +pub struct Iter<'a, T: Item> { + tree: &'a SumTree, + stack: ArrayVec, 16>, +} + +impl<'a, 'b, T, D> Cursor<'a, 'b, T, D> +where + T: Item, + D: Dimension<'a, T::Summary>, +{ + pub fn new(tree: &'a SumTree, cx: ::Context<'b>) -> Self { + Self { + tree, + stack: ArrayVec::new(), + position: D::zero(cx), + did_seek: false, + at_end: tree.is_empty(), + cx, + } + } + + fn reset(&mut self) { + self.did_seek = false; + self.at_end = self.tree.is_empty(); + self.stack.truncate(0); + self.position = D::zero(self.cx); + } + + pub fn start(&self) -> &D { + &self.position + } + + #[track_caller] + pub fn end(&self) -> D { + if let Some(item_summary) = self.item_summary() { + let mut end = self.start().clone(); + end.add_summary(item_summary, self.cx); + end + } else { + self.start().clone() + } + } + + /// Item is None, when the list is empty, or this cursor is at the end of the list. + #[track_caller] + pub fn item(&self) -> Option<&'a T> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + match *entry.tree.0 { + Node::Leaf { ref items, .. } => { + if entry.index() == items.len() { + None + } else { + Some(&items[entry.index()]) + } + } + _ => unreachable!(), + } + } else { + None + } + } + + #[track_caller] + pub fn item_summary(&self) -> Option<&'a T::Summary> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + match *entry.tree.0 { + Node::Leaf { + ref item_summaries, .. + } => { + if entry.index() == item_summaries.len() { + None + } else { + Some(&item_summaries[entry.index()]) + } + } + _ => unreachable!(), + } + } else { + None + } + } + + #[track_caller] + pub fn next_item(&self) -> Option<&'a T> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + if entry.index() == entry.tree.0.items().len() - 1 { + if let Some(next_leaf) = self.next_leaf() { + Some(next_leaf.0.items().first().unwrap()) + } else { + None + } + } else { + match *entry.tree.0 { + Node::Leaf { ref items, .. } => Some(&items[entry.index() + 1]), + _ => unreachable!(), + } + } + } else if self.at_end { + None + } else { + self.tree.first() + } + } + + #[track_caller] + fn next_leaf(&self) -> Option<&'a SumTree> { + for entry in self.stack.iter().rev().skip(1) { + if entry.index() < entry.tree.0.child_trees().len() - 1 { + match *entry.tree.0 { + Node::Internal { + ref child_trees, .. + } => return Some(child_trees[entry.index() + 1].leftmost_leaf()), + Node::Leaf { .. } => unreachable!(), + }; + } + } + None + } + + #[track_caller] + pub fn prev_item(&self) -> Option<&'a T> { + self.assert_did_seek(); + if let Some(entry) = self.stack.last() { + if entry.index() == 0 { + if let Some(prev_leaf) = self.prev_leaf() { + Some(prev_leaf.0.items().last().unwrap()) + } else { + None + } + } else { + match *entry.tree.0 { + Node::Leaf { ref items, .. } => Some(&items[entry.index() - 1]), + _ => unreachable!(), + } + } + } else if self.at_end { + self.tree.last() + } else { + None + } + } + + #[track_caller] + fn prev_leaf(&self) -> Option<&'a SumTree> { + for entry in self.stack.iter().rev().skip(1) { + if entry.index() != 0 { + match *entry.tree.0 { + Node::Internal { + ref child_trees, .. + } => return Some(child_trees[entry.index() - 1].rightmost_leaf()), + Node::Leaf { .. } => unreachable!(), + }; + } + } + None + } + + #[track_caller] + pub fn prev(&mut self) { + self.search_backward(|_| true) + } + + #[track_caller] + pub fn search_backward(&mut self, mut filter_node: F) + where + F: FnMut(&T::Summary) -> bool, + { + if !self.did_seek { + self.did_seek = true; + self.at_end = true; + } + + if self.at_end { + self.position = D::zero(self.cx); + self.at_end = self.tree.is_empty(); + if !self.tree.is_empty() { + self.stack.push(StackEntry { + tree: self.tree, + index: self.tree.0.child_summaries().len() as u32, + position: D::from_summary(self.tree.summary(), self.cx), + }); + } + } + + let mut descending = false; + while !self.stack.is_empty() { + if let Some(StackEntry { position, .. }) = self.stack.iter().rev().nth(1) { + self.position = position.clone(); + } else { + self.position = D::zero(self.cx); + } + + let entry = self.stack.last_mut().unwrap(); + if !descending { + if entry.index() == 0 { + self.stack.pop(); + continue; + } else { + entry.index -= 1; + } + } + + for summary in &entry.tree.0.child_summaries()[..entry.index()] { + self.position.add_summary(summary, self.cx); + } + entry.position = self.position.clone(); + + descending = filter_node(&entry.tree.0.child_summaries()[entry.index()]); + match entry.tree.0.as_ref() { + Node::Internal { child_trees, .. } => { + if descending { + let tree = &child_trees[entry.index()]; + self.stack.push(StackEntry { + position: D::zero(self.cx), + tree, + index: tree.0.child_summaries().len() as u32 - 1, + }) + } + } + Node::Leaf { .. } => { + if descending { + break; + } + } + } + } + } + + #[track_caller] + pub fn next(&mut self) { + self.search_forward(|_| true) + } + + #[track_caller] + pub fn search_forward(&mut self, mut filter_node: F) + where + F: FnMut(&T::Summary) -> bool, + { + let mut descend = false; + + if self.stack.is_empty() { + if !self.at_end { + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + position: D::zero(self.cx), + }); + descend = true; + } + self.did_seek = true; + } + + while !self.stack.is_empty() { + let new_subtree = { + let entry = self.stack.last_mut().unwrap(); + match entry.tree.0.as_ref() { + Node::Internal { + child_trees, + child_summaries, + .. + } => { + if !descend { + entry.index += 1; + entry.position = self.position.clone(); + } + + while entry.index() < child_summaries.len() { + let next_summary = &child_summaries[entry.index()]; + if filter_node(next_summary) { + break; + } else { + entry.index += 1; + entry.position.add_summary(next_summary, self.cx); + self.position.add_summary(next_summary, self.cx); + } + } + + child_trees.get(entry.index()) + } + Node::Leaf { item_summaries, .. } => { + if !descend { + let item_summary = &item_summaries[entry.index()]; + entry.index += 1; + entry.position.add_summary(item_summary, self.cx); + self.position.add_summary(item_summary, self.cx); + } + + loop { + if let Some(next_item_summary) = item_summaries.get(entry.index()) { + if filter_node(next_item_summary) { + return; + } else { + entry.index += 1; + entry.position.add_summary(next_item_summary, self.cx); + self.position.add_summary(next_item_summary, self.cx); + } + } else { + break None; + } + } + } + } + }; + + if let Some(subtree) = new_subtree { + descend = true; + self.stack.push(StackEntry { + tree: subtree, + index: 0, + position: self.position.clone(), + }); + } else { + descend = false; + self.stack.pop(); + } + } + + self.at_end = self.stack.is_empty(); + debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf()); + } + + #[track_caller] + fn assert_did_seek(&self) { + assert!( + self.did_seek, + "Must call `seek`, `next` or `prev` before calling this method" + ); + } +} + +impl<'a, 'b, T, D> Cursor<'a, 'b, T, D> +where + T: Item, + D: Dimension<'a, T::Summary>, +{ + #[track_caller] + pub fn seek(&mut self, pos: &Target, bias: Bias) -> bool + where + Target: SeekTarget<'a, T::Summary, D>, + { + self.reset(); + self.seek_internal(pos, bias, &mut ()) + } + + #[track_caller] + pub fn seek_forward(&mut self, pos: &Target, bias: Bias) -> bool + where + Target: SeekTarget<'a, T::Summary, D>, + { + self.seek_internal(pos, bias, &mut ()) + } + + /// Advances the cursor and returns traversed items as a tree. + #[track_caller] + pub fn slice(&mut self, end: &Target, bias: Bias) -> SumTree + where + Target: SeekTarget<'a, T::Summary, D>, + { + let mut slice = SliceSeekAggregate { + tree: SumTree::new(self.cx), + leaf_items: ArrayVec::new(), + leaf_item_summaries: ArrayVec::new(), + leaf_summary: ::zero(self.cx), + }; + self.seek_internal(end, bias, &mut slice); + slice.tree + } + + #[track_caller] + pub fn suffix(&mut self) -> SumTree { + self.slice(&End::new(), Bias::Right) + } + + #[track_caller] + pub fn summary(&mut self, end: &Target, bias: Bias) -> Output + where + Target: SeekTarget<'a, T::Summary, D>, + Output: Dimension<'a, T::Summary>, + { + let mut summary = SummarySeekAggregate(Output::zero(self.cx)); + self.seek_internal(end, bias, &mut summary); + summary.0 + } + + /// Returns whether we found the item you were seeking for + #[track_caller] + fn seek_internal( + &mut self, + target: &dyn SeekTarget<'a, T::Summary, D>, + bias: Bias, + aggregate: &mut dyn SeekAggregate<'a, T>, + ) -> bool { + assert!( + target.cmp(&self.position, self.cx) >= Ordering::Equal, + "cannot seek backward", + ); + + if !self.did_seek { + self.did_seek = true; + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + position: D::zero(self.cx), + }); + } + + let mut ascending = false; + 'outer: while let Some(entry) = self.stack.last_mut() { + match *entry.tree.0 { + Node::Internal { + ref child_summaries, + ref child_trees, + .. + } => { + if ascending { + entry.index += 1; + entry.position = self.position.clone(); + } + + for (child_tree, child_summary) in child_trees[entry.index()..] + .iter() + .zip(&child_summaries[entry.index()..]) + { + let mut child_end = self.position.clone(); + child_end.add_summary(child_summary, self.cx); + + let comparison = target.cmp(&child_end, self.cx); + if comparison == Ordering::Greater + || (comparison == Ordering::Equal && bias == Bias::Right) + { + self.position = child_end; + aggregate.push_tree(child_tree, child_summary, self.cx); + entry.index += 1; + entry.position = self.position.clone(); + } else { + self.stack.push(StackEntry { + tree: child_tree, + index: 0, + position: self.position.clone(), + }); + ascending = false; + continue 'outer; + } + } + } + Node::Leaf { + ref items, + ref item_summaries, + .. + } => { + aggregate.begin_leaf(); + + for (item, item_summary) in items[entry.index()..] + .iter() + .zip(&item_summaries[entry.index()..]) + { + let mut child_end = self.position.clone(); + child_end.add_summary(item_summary, self.cx); + + let comparison = target.cmp(&child_end, self.cx); + if comparison == Ordering::Greater + || (comparison == Ordering::Equal && bias == Bias::Right) + { + self.position = child_end; + aggregate.push_item(item, item_summary, self.cx); + entry.index += 1; + } else { + aggregate.end_leaf(self.cx); + break 'outer; + } + } + + aggregate.end_leaf(self.cx); + } + } + + self.stack.pop(); + ascending = true; + } + + self.at_end = self.stack.is_empty(); + debug_assert!(self.stack.is_empty() || self.stack.last().unwrap().tree.0.is_leaf()); + + let mut end = self.position.clone(); + if bias == Bias::Left + && let Some(summary) = self.item_summary() + { + end.add_summary(summary, self.cx); + } + + target.cmp(&end, self.cx) == Ordering::Equal + } +} + +impl<'a, T: Item> Iter<'a, T> { + pub(crate) fn new(tree: &'a SumTree) -> Self { + Self { + tree, + stack: Default::default(), + } + } +} + +impl<'a, T: Item> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + let mut descend = false; + + if self.stack.is_empty() { + self.stack.push(StackEntry { + tree: self.tree, + index: 0, + position: (), + }); + descend = true; + } + + while !self.stack.is_empty() { + let new_subtree = { + let entry = self.stack.last_mut().unwrap(); + match entry.tree.0.as_ref() { + Node::Internal { child_trees, .. } => { + if !descend { + entry.index += 1; + } + child_trees.get(entry.index()) + } + Node::Leaf { items, .. } => { + if !descend { + entry.index += 1; + } + + if let Some(next_item) = items.get(entry.index()) { + return Some(next_item); + } else { + None + } + } + } + }; + + if let Some(subtree) = new_subtree { + descend = true; + self.stack.push(StackEntry { + tree: subtree, + index: 0, + position: (), + }); + } else { + descend = false; + self.stack.pop(); + } + } + + None + } +} + +impl<'a, 'b, T: Item, D> Iterator for Cursor<'a, 'b, T, D> +where + D: Dimension<'a, T::Summary>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if !self.did_seek { + self.next(); + } + + if let Some(item) = self.item() { + self.next(); + Some(item) + } else { + None + } + } +} + +pub struct FilterCursor<'a, 'b, F, T: Item, D> { + cursor: Cursor<'a, 'b, T, D>, + filter_node: F, +} + +impl<'a, 'b, F, T: Item, D> FilterCursor<'a, 'b, F, T, D> +where + F: FnMut(&T::Summary) -> bool, + T: Item, + D: Dimension<'a, T::Summary>, +{ + pub fn new( + tree: &'a SumTree, + cx: ::Context<'b>, + filter_node: F, + ) -> Self { + let cursor = tree.cursor::(cx); + Self { + cursor, + filter_node, + } + } + + pub fn start(&self) -> &D { + self.cursor.start() + } + + pub fn end(&self) -> D { + self.cursor.end() + } + + pub fn item(&self) -> Option<&'a T> { + self.cursor.item() + } + + pub fn item_summary(&self) -> Option<&'a T::Summary> { + self.cursor.item_summary() + } + + pub fn next(&mut self) { + self.cursor.search_forward(&mut self.filter_node); + } + + pub fn prev(&mut self) { + self.cursor.search_backward(&mut self.filter_node); + } +} + +impl<'a, 'b, F, T: Item, U> Iterator for FilterCursor<'a, 'b, F, T, U> +where + F: FnMut(&T::Summary) -> bool, + U: Dimension<'a, T::Summary>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if !self.cursor.did_seek { + self.next(); + } + + if let Some(item) = self.item() { + self.cursor.search_forward(&mut self.filter_node); + Some(item) + } else { + None + } + } +} + +trait SeekAggregate<'a, T: Item> { + fn begin_leaf(&mut self); + fn end_leaf(&mut self, cx: ::Context<'_>); + fn push_item( + &mut self, + item: &'a T, + summary: &'a T::Summary, + cx: ::Context<'_>, + ); + fn push_tree( + &mut self, + tree: &'a SumTree, + summary: &'a T::Summary, + cx: ::Context<'_>, + ); +} + +struct SliceSeekAggregate { + tree: SumTree, + leaf_items: ArrayVec, + leaf_item_summaries: ArrayVec, + leaf_summary: T::Summary, +} + +struct SummarySeekAggregate(D); + +impl SeekAggregate<'_, T> for () { + fn begin_leaf(&mut self) {} + fn end_leaf(&mut self, _: ::Context<'_>) {} + fn push_item(&mut self, _: &T, _: &T::Summary, _: ::Context<'_>) {} + fn push_tree( + &mut self, + _: &SumTree, + _: &T::Summary, + _: ::Context<'_>, + ) { + } +} + +impl SeekAggregate<'_, T> for SliceSeekAggregate { + fn begin_leaf(&mut self) {} + fn end_leaf(&mut self, cx: ::Context<'_>) { + self.tree.append( + SumTree(Arc::new(Node::Leaf { + summary: mem::replace(&mut self.leaf_summary, ::zero(cx)), + items: mem::take(&mut self.leaf_items), + item_summaries: mem::take(&mut self.leaf_item_summaries), + })), + cx, + ); + } + fn push_item( + &mut self, + item: &T, + summary: &T::Summary, + cx: ::Context<'_>, + ) { + self.leaf_items.push(item.clone()); + self.leaf_item_summaries.push(summary.clone()); + Summary::add_summary(&mut self.leaf_summary, summary, cx); + } + fn push_tree( + &mut self, + tree: &SumTree, + _: &T::Summary, + cx: ::Context<'_>, + ) { + self.tree.append(tree.clone(), cx); + } +} + +impl<'a, T: Item, D> SeekAggregate<'a, T> for SummarySeekAggregate +where + D: Dimension<'a, T::Summary>, +{ + fn begin_leaf(&mut self) {} + fn end_leaf(&mut self, _: ::Context<'_>) {} + fn push_item( + &mut self, + _: &T, + summary: &'a T::Summary, + cx: ::Context<'_>, + ) { + self.0.add_summary(summary, cx); + } + fn push_tree( + &mut self, + _: &SumTree, + summary: &'a T::Summary, + cx: ::Context<'_>, + ) { + self.0.add_summary(summary, cx); + } +} + +struct End(PhantomData); + +impl End { + fn new() -> Self { + Self(PhantomData) + } +} + +impl<'a, S: Summary, D: Dimension<'a, S>> SeekTarget<'a, S, D> for End { + fn cmp(&self, _: &D, _: S::Context<'_>) -> Ordering { + Ordering::Greater + } +} + +impl fmt::Debug for End { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("End").finish() + } +} diff --git a/third_party/gpui_support/zed-sum-tree/src/sum_tree.rs b/third_party/gpui_support/zed-sum-tree/src/sum_tree.rs new file mode 100644 index 0000000..bfd2423 --- /dev/null +++ b/third_party/gpui_support/zed-sum-tree/src/sum_tree.rs @@ -0,0 +1,1444 @@ +mod cursor; +mod tree_map; + +use arrayvec::ArrayVec; +pub use cursor::{Cursor, FilterCursor, Iter}; +use rayon::prelude::*; +use std::marker::PhantomData; +use std::mem; +use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc}; +pub use tree_map::{MapSeekTarget, TreeMap, TreeSet}; + +#[cfg(test)] +pub const TREE_BASE: usize = 2; +#[cfg(not(test))] +pub const TREE_BASE: usize = 6; + +/// An item that can be stored in a [`SumTree`] +/// +/// Must be summarized by a type that implements [`Summary`] +pub trait Item: Clone { + type Summary: Summary; + + fn summary(&self, cx: ::Context<'_>) -> Self::Summary; +} + +/// An [`Item`] whose summary has a specific key that can be used to identify it +pub trait KeyedItem: Item { + type Key: for<'a> Dimension<'a, Self::Summary> + Ord; + + fn key(&self) -> Self::Key; +} + +/// A type that describes the Sum of all [`Item`]s in a subtree of the [`SumTree`] +/// +/// Each Summary type can have multiple [`Dimension`]s that it measures, +/// which can be used to navigate the tree +pub trait Summary: Clone { + type Context<'a>: Copy; + fn zero<'a>(cx: Self::Context<'a>) -> Self; + fn add_summary<'a>(&mut self, summary: &Self, cx: Self::Context<'a>); +} + +pub trait ContextLessSummary: Clone { + fn zero() -> Self; + fn add_summary(&mut self, summary: &Self); +} + +impl Summary for T { + type Context<'a> = (); + + fn zero<'a>((): ()) -> Self { + T::zero() + } + + fn add_summary<'a>(&mut self, summary: &Self, (): ()) { + T::add_summary(self, summary) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct NoSummary; + +/// Catch-all implementation for when you need something that implements [`Summary`] without a specific type. +/// We implement it on a `NoSummary` instead of re-using `()`, as that avoids blanket impl collisions with `impl Dimension for T` +/// (as we also need unit type to be a fill-in dimension) +impl ContextLessSummary for NoSummary { + fn zero() -> Self { + NoSummary + } + + fn add_summary(&mut self, _: &Self) {} +} + +/// Each [`Summary`] type can have more than one [`Dimension`] type that it measures. +/// +/// You can use dimensions to seek to a specific location in the [`SumTree`] +/// +/// # Example: +/// Zed's rope has a `TextSummary` type that summarizes lines, characters, and bytes. +/// Each of these are different dimensions we may want to seek to +pub trait Dimension<'a, S: Summary>: Clone { + fn zero(cx: S::Context<'_>) -> Self; + + fn add_summary(&mut self, summary: &'a S, cx: S::Context<'_>); + + fn from_summary(summary: &'a S, cx: S::Context<'_>) -> Self { + let mut dimension = Self::zero(cx); + dimension.add_summary(summary, cx); + dimension + } +} + +impl<'a, T: Summary> Dimension<'a, T> for T { + fn zero(cx: T::Context<'_>) -> Self { + Summary::zero(cx) + } + + fn add_summary(&mut self, summary: &'a T, cx: T::Context<'_>) { + Summary::add_summary(self, summary, cx); + } +} + +pub trait SeekTarget<'a, S: Summary, D: Dimension<'a, S>> { + fn cmp(&self, cursor_location: &D, cx: S::Context<'_>) -> Ordering; +} + +impl<'a, S: Summary, D: Dimension<'a, S> + Ord> SeekTarget<'a, S, D> for D { + fn cmp(&self, cursor_location: &Self, _: S::Context<'_>) -> Ordering { + Ord::cmp(self, cursor_location) + } +} + +impl<'a, T: Summary> Dimension<'a, T> for () { + fn zero(_: T::Context<'_>) -> Self {} + + fn add_summary(&mut self, _: &'a T, _: T::Context<'_>) {} +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Dimensions(pub D1, pub D2, pub D3); + +impl<'a, T: Summary, D1: Dimension<'a, T>, D2: Dimension<'a, T>, D3: Dimension<'a, T>> + Dimension<'a, T> for Dimensions +{ + fn zero(cx: T::Context<'_>) -> Self { + Dimensions(D1::zero(cx), D2::zero(cx), D3::zero(cx)) + } + + fn add_summary(&mut self, summary: &'a T, cx: T::Context<'_>) { + self.0.add_summary(summary, cx); + self.1.add_summary(summary, cx); + self.2.add_summary(summary, cx); + } +} + +impl<'a, S, D1, D2, D3> SeekTarget<'a, S, Dimensions> for D1 +where + S: Summary, + D1: SeekTarget<'a, S, D1> + Dimension<'a, S>, + D2: Dimension<'a, S>, + D3: Dimension<'a, S>, +{ + fn cmp(&self, cursor_location: &Dimensions, cx: S::Context<'_>) -> Ordering { + self.cmp(&cursor_location.0, cx) + } +} + +/// Bias is used to settle ambiguities when determining positions in an ordered sequence. +/// +/// The primary use case is for text, where Bias influences +/// which character an offset or anchor is associated with. +/// +/// # Examples +/// Given the buffer `AˇBCD`: +/// - The offset of the cursor is 1 +/// - [Bias::Left] would attach the cursor to the character `A` +/// - [Bias::Right] would attach the cursor to the character `B` +/// +/// Given the buffer `A«BCˇ»D`: +/// - The offset of the cursor is 3, and the selection is from 1 to 3 +/// - The left anchor of the selection has [Bias::Right], attaching it to the character `B` +/// - The right anchor of the selection has [Bias::Left], attaching it to the character `C` +/// +/// Given the buffer `{ˇ<...>`, where `<...>` is a folded region: +/// - The display offset of the cursor is 1, but the offset in the buffer is determined by the bias +/// - [Bias::Left] would attach the cursor to the character `{`, with a buffer offset of 1 +/// - [Bias::Right] would attach the cursor to the first character of the folded region, +/// and the buffer offset would be the offset of the first character of the folded region +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Default)] +pub enum Bias { + /// Attach to the character on the left + #[default] + Left, + /// Attach to the character on the right + Right, +} + +impl Bias { + pub fn invert(self) -> Self { + match self { + Self::Left => Self::Right, + Self::Right => Self::Left, + } + } +} + +/// A B+ tree in which each leaf node contains `Item`s of type `T` and a `Summary`s for each `Item`. +/// Each internal node contains a `Summary` of the items in its subtree. +/// +/// The maximum number of items per node is `TREE_BASE * 2`. +/// +/// Any [`Dimension`] supported by the [`Summary`] type can be used to seek to a specific location in the tree. +#[derive(Clone)] +pub struct SumTree(Arc>); + +impl fmt::Debug for SumTree +where + T: fmt::Debug + Item, + T::Summary: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SumTree").field(&self.0).finish() + } +} + +impl SumTree { + pub fn new(cx: ::Context<'_>) -> Self { + SumTree(Arc::new(Node::Leaf { + summary: ::zero(cx), + items: ArrayVec::new(), + item_summaries: ArrayVec::new(), + })) + } + + /// Useful in cases where the item type has a non-trivial context type, but the zero value of the summary type doesn't depend on that context. + pub fn from_summary(summary: T::Summary) -> Self { + SumTree(Arc::new(Node::Leaf { + summary, + items: ArrayVec::new(), + item_summaries: ArrayVec::new(), + })) + } + + pub fn from_item(item: T, cx: ::Context<'_>) -> Self { + let mut tree = Self::new(cx); + tree.push(item, cx); + tree + } + + pub fn from_iter>( + iter: I, + cx: ::Context<'_>, + ) -> Self { + let mut nodes = Vec::new(); + + let mut iter = iter.into_iter().fuse().peekable(); + while iter.peek().is_some() { + let items: ArrayVec = iter.by_ref().take(2 * TREE_BASE).collect(); + let item_summaries: ArrayVec = + items.iter().map(|item| item.summary(cx)).collect(); + + let mut summary = item_summaries[0].clone(); + for item_summary in &item_summaries[1..] { + ::add_summary(&mut summary, item_summary, cx); + } + + nodes.push(Node::Leaf { + summary, + items, + item_summaries, + }); + } + + let mut parent_nodes = Vec::new(); + let mut height = 0; + while nodes.len() > 1 { + height += 1; + let mut current_parent_node = None; + for child_node in nodes.drain(..) { + let parent_node = current_parent_node.get_or_insert_with(|| Node::Internal { + summary: ::zero(cx), + height, + child_summaries: ArrayVec::new(), + child_trees: ArrayVec::new(), + }); + let Node::Internal { + summary, + child_summaries, + child_trees, + .. + } = parent_node + else { + unreachable!() + }; + let child_summary = child_node.summary(); + ::add_summary(summary, child_summary, cx); + child_summaries.push(child_summary.clone()); + child_trees.push(Self(Arc::new(child_node))); + + if child_trees.len() == 2 * TREE_BASE { + parent_nodes.extend(current_parent_node.take()); + } + } + parent_nodes.extend(current_parent_node.take()); + mem::swap(&mut nodes, &mut parent_nodes); + } + + if nodes.is_empty() { + Self::new(cx) + } else { + debug_assert_eq!(nodes.len(), 1); + Self(Arc::new(nodes.pop().unwrap())) + } + } + + pub fn from_par_iter(iter: I, cx: ::Context<'_>) -> Self + where + I: IntoParallelIterator, + Iter: IndexedParallelIterator, + T: Send + Sync, + T::Summary: Send + Sync, + for<'a> ::Context<'a>: Sync, + { + let mut nodes = iter + .into_par_iter() + .chunks(2 * TREE_BASE) + .map(|items| { + let items: ArrayVec = items.into_iter().collect(); + let item_summaries: ArrayVec = + items.iter().map(|item| item.summary(cx)).collect(); + let mut summary = item_summaries[0].clone(); + for item_summary in &item_summaries[1..] { + ::add_summary(&mut summary, item_summary, cx); + } + SumTree(Arc::new(Node::Leaf { + summary, + items, + item_summaries, + })) + }) + .collect::>(); + + let mut height = 0; + while nodes.len() > 1 { + height += 1; + nodes = nodes + .into_par_iter() + .chunks(2 * TREE_BASE) + .map(|child_nodes| { + let child_trees: ArrayVec, { 2 * TREE_BASE }> = + child_nodes.into_iter().collect(); + let child_summaries: ArrayVec = child_trees + .iter() + .map(|child_tree| child_tree.summary().clone()) + .collect(); + let mut summary = child_summaries[0].clone(); + for child_summary in &child_summaries[1..] { + ::add_summary(&mut summary, child_summary, cx); + } + SumTree(Arc::new(Node::Internal { + height, + summary, + child_summaries, + child_trees, + })) + }) + .collect::>(); + } + + if nodes.is_empty() { + Self::new(cx) + } else { + debug_assert_eq!(nodes.len(), 1); + nodes.pop().unwrap() + } + } + + #[allow(unused)] + pub fn items<'a>(&'a self, cx: ::Context<'a>) -> Vec { + let mut items = Vec::new(); + let mut cursor = self.cursor::<()>(cx); + cursor.next(); + while let Some(item) = cursor.item() { + items.push(item.clone()); + cursor.next(); + } + items + } + + pub fn iter(&self) -> Iter<'_, T> { + Iter::new(self) + } + + pub fn cursor<'a, 'b, S>( + &'a self, + cx: ::Context<'b>, + ) -> Cursor<'a, 'b, T, S> + where + S: Dimension<'a, T::Summary>, + { + Cursor::new(self, cx) + } + + /// Note: If the summary type requires a non `()` context, then the filter cursor + /// that is returned cannot be used with Rust's iterators. + pub fn filter<'a, 'b, F, U>( + &'a self, + cx: ::Context<'b>, + filter_node: F, + ) -> FilterCursor<'a, 'b, F, T, U> + where + F: FnMut(&T::Summary) -> bool, + U: Dimension<'a, T::Summary>, + { + FilterCursor::new(self, cx, filter_node) + } + + #[allow(dead_code)] + pub fn first(&self) -> Option<&T> { + self.leftmost_leaf().0.items().first() + } + + pub fn last(&self) -> Option<&T> { + self.rightmost_leaf().0.items().last() + } + + pub fn update_last( + &mut self, + f: impl FnOnce(&mut T), + cx: ::Context<'_>, + ) { + self.update_last_recursive(f, cx); + } + + fn update_last_recursive( + &mut self, + f: impl FnOnce(&mut T), + cx: ::Context<'_>, + ) -> Option { + match Arc::make_mut(&mut self.0) { + Node::Internal { + summary, + child_summaries, + child_trees, + .. + } => { + let last_summary = child_summaries.last_mut().unwrap(); + let last_child = child_trees.last_mut().unwrap(); + *last_summary = last_child.update_last_recursive(f, cx).unwrap(); + *summary = sum(child_summaries.iter(), cx); + Some(summary.clone()) + } + Node::Leaf { + summary, + items, + item_summaries, + } => { + if let Some((item, item_summary)) = items.last_mut().zip(item_summaries.last_mut()) + { + (f)(item); + *item_summary = item.summary(cx); + *summary = sum(item_summaries.iter(), cx); + Some(summary.clone()) + } else { + None + } + } + } + } + + pub fn extent<'a, D: Dimension<'a, T::Summary>>( + &'a self, + cx: ::Context<'_>, + ) -> D { + let mut extent = D::zero(cx); + match self.0.as_ref() { + Node::Internal { summary, .. } | Node::Leaf { summary, .. } => { + extent.add_summary(summary, cx); + } + } + extent + } + + pub fn summary(&self) -> &T::Summary { + match self.0.as_ref() { + Node::Internal { summary, .. } => summary, + Node::Leaf { summary, .. } => summary, + } + } + + pub fn is_empty(&self) -> bool { + match self.0.as_ref() { + Node::Internal { .. } => false, + Node::Leaf { items, .. } => items.is_empty(), + } + } + + pub fn extend(&mut self, iter: I, cx: ::Context<'_>) + where + I: IntoIterator, + { + self.append(Self::from_iter(iter, cx), cx); + } + + pub fn par_extend(&mut self, iter: I, cx: ::Context<'_>) + where + I: IntoParallelIterator, + Iter: IndexedParallelIterator, + T: Send + Sync, + T::Summary: Send + Sync, + for<'a> ::Context<'a>: Sync, + { + self.append(Self::from_par_iter(iter, cx), cx); + } + + pub fn push(&mut self, item: T, cx: ::Context<'_>) { + let summary = item.summary(cx); + self.append( + SumTree(Arc::new(Node::Leaf { + summary: summary.clone(), + items: ArrayVec::from_iter(Some(item)), + item_summaries: ArrayVec::from_iter(Some(summary)), + })), + cx, + ); + } + + pub fn append(&mut self, other: Self, cx: ::Context<'_>) { + if self.is_empty() { + *self = other; + } else if !other.0.is_leaf() || !other.0.items().is_empty() { + if self.0.height() < other.0.height() { + for tree in other.0.child_trees() { + self.append(tree.clone(), cx); + } + } else if let Some(split_tree) = self.push_tree_recursive(other, cx) { + *self = Self::from_child_trees(self.clone(), split_tree, cx); + } + } + } + + fn push_tree_recursive( + &mut self, + other: SumTree, + cx: ::Context<'_>, + ) -> Option> { + match Arc::make_mut(&mut self.0) { + Node::Internal { + height, + summary, + child_summaries, + child_trees, + .. + } => { + let other_node = other.0.clone(); + ::add_summary(summary, other_node.summary(), cx); + + let height_delta = *height - other_node.height(); + let mut summaries_to_append = ArrayVec::::new(); + let mut trees_to_append = ArrayVec::, { 2 * TREE_BASE }>::new(); + if height_delta == 0 { + summaries_to_append.extend(other_node.child_summaries().iter().cloned()); + trees_to_append.extend(other_node.child_trees().iter().cloned()); + } else if height_delta == 1 && !other_node.is_underflowing() { + summaries_to_append.push(other_node.summary().clone()); + trees_to_append.push(other) + } else { + let tree_to_append = child_trees + .last_mut() + .unwrap() + .push_tree_recursive(other, cx); + *child_summaries.last_mut().unwrap() = + child_trees.last().unwrap().0.summary().clone(); + + if let Some(split_tree) = tree_to_append { + summaries_to_append.push(split_tree.0.summary().clone()); + trees_to_append.push(split_tree); + } + } + + let child_count = child_trees.len() + trees_to_append.len(); + if child_count > 2 * TREE_BASE { + let left_summaries: ArrayVec<_, { 2 * TREE_BASE }>; + let right_summaries: ArrayVec<_, { 2 * TREE_BASE }>; + let left_trees; + let right_trees; + + let midpoint = (child_count + child_count % 2) / 2; + { + let mut all_summaries = child_summaries + .iter() + .chain(summaries_to_append.iter()) + .cloned(); + left_summaries = all_summaries.by_ref().take(midpoint).collect(); + right_summaries = all_summaries.collect(); + let mut all_trees = + child_trees.iter().chain(trees_to_append.iter()).cloned(); + left_trees = all_trees.by_ref().take(midpoint).collect(); + right_trees = all_trees.collect(); + } + *summary = sum(left_summaries.iter(), cx); + *child_summaries = left_summaries; + *child_trees = left_trees; + + Some(SumTree(Arc::new(Node::Internal { + height: *height, + summary: sum(right_summaries.iter(), cx), + child_summaries: right_summaries, + child_trees: right_trees, + }))) + } else { + child_summaries.extend(summaries_to_append); + child_trees.extend(trees_to_append); + None + } + } + Node::Leaf { + summary, + items, + item_summaries, + } => { + let other_node = other.0; + + let child_count = items.len() + other_node.items().len(); + if child_count > 2 * TREE_BASE { + let left_items; + let right_items; + let left_summaries; + let right_summaries: ArrayVec; + + let midpoint = (child_count + child_count % 2) / 2; + { + let mut all_items = items.iter().chain(other_node.items().iter()).cloned(); + left_items = all_items.by_ref().take(midpoint).collect(); + right_items = all_items.collect(); + + let mut all_summaries = item_summaries + .iter() + .chain(other_node.child_summaries()) + .cloned(); + left_summaries = all_summaries.by_ref().take(midpoint).collect(); + right_summaries = all_summaries.collect(); + } + *items = left_items; + *item_summaries = left_summaries; + *summary = sum(item_summaries.iter(), cx); + Some(SumTree(Arc::new(Node::Leaf { + items: right_items, + summary: sum(right_summaries.iter(), cx), + item_summaries: right_summaries, + }))) + } else { + ::add_summary(summary, other_node.summary(), cx); + items.extend(other_node.items().iter().cloned()); + item_summaries.extend(other_node.child_summaries().iter().cloned()); + None + } + } + } + } + + fn from_child_trees( + left: SumTree, + right: SumTree, + cx: ::Context<'_>, + ) -> Self { + let height = left.0.height() + 1; + let mut child_summaries = ArrayVec::new(); + child_summaries.push(left.0.summary().clone()); + child_summaries.push(right.0.summary().clone()); + let mut child_trees = ArrayVec::new(); + child_trees.push(left); + child_trees.push(right); + SumTree(Arc::new(Node::Internal { + height, + summary: sum(child_summaries.iter(), cx), + child_summaries, + child_trees, + })) + } + + fn leftmost_leaf(&self) -> &Self { + match *self.0 { + Node::Leaf { .. } => self, + Node::Internal { + ref child_trees, .. + } => child_trees.first().unwrap().leftmost_leaf(), + } + } + + fn rightmost_leaf(&self) -> &Self { + match *self.0 { + Node::Leaf { .. } => self, + Node::Internal { + ref child_trees, .. + } => child_trees.last().unwrap().rightmost_leaf(), + } + } +} + +impl PartialEq for SumTree { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} + +impl Eq for SumTree {} + +impl SumTree { + pub fn insert_or_replace<'a, 'b>( + &'a mut self, + item: T, + cx: ::Context<'b>, + ) -> Option { + let mut replaced = None; + { + let mut cursor = self.cursor::(cx); + let mut new_tree = cursor.slice(&item.key(), Bias::Left); + if let Some(cursor_item) = cursor.item() + && cursor_item.key() == item.key() + { + replaced = Some(cursor_item.clone()); + cursor.next(); + } + new_tree.push(item, cx); + new_tree.append(cursor.suffix(), cx); + drop(cursor); + *self = new_tree + }; + replaced + } + + pub fn remove(&mut self, key: &T::Key, cx: ::Context<'_>) -> Option { + let mut removed = None; + *self = { + let mut cursor = self.cursor::(cx); + let mut new_tree = cursor.slice(key, Bias::Left); + if let Some(item) = cursor.item() + && item.key() == *key + { + removed = Some(item.clone()); + cursor.next(); + } + new_tree.append(cursor.suffix(), cx); + new_tree + }; + removed + } + + pub fn edit( + &mut self, + mut edits: Vec>, + cx: ::Context<'_>, + ) -> Vec { + if edits.is_empty() { + return Vec::new(); + } + + let mut removed = Vec::new(); + edits.sort_unstable_by_key(|item| item.key()); + + *self = { + let mut cursor = self.cursor::(cx); + let mut new_tree = SumTree::new(cx); + let mut buffered_items = Vec::new(); + + cursor.seek(&T::Key::zero(cx), Bias::Left); + for edit in edits { + let new_key = edit.key(); + let mut old_item = cursor.item(); + + if old_item + .as_ref() + .is_some_and(|old_item| old_item.key() < new_key) + { + new_tree.extend(buffered_items.drain(..), cx); + let slice = cursor.slice(&new_key, Bias::Left); + new_tree.append(slice, cx); + old_item = cursor.item(); + } + + if let Some(old_item) = old_item + && old_item.key() == new_key + { + removed.push(old_item.clone()); + cursor.next(); + } + + match edit { + Edit::Insert(item) => { + buffered_items.push(item); + } + Edit::Remove(_) => {} + } + } + + new_tree.extend(buffered_items, cx); + new_tree.append(cursor.suffix(), cx); + new_tree + }; + + removed + } + + pub fn get<'a>( + &'a self, + key: &T::Key, + cx: ::Context<'a>, + ) -> Option<&'a T> { + let mut cursor = self.cursor::(cx); + if cursor.seek(key, Bias::Left) { + cursor.item() + } else { + None + } + } +} + +impl Default for SumTree +where + T: Item, + S: for<'a> Summary = ()>, +{ + fn default() -> Self { + Self::new(()) + } +} + +#[derive(Clone)] +pub enum Node { + Internal { + height: u8, + summary: T::Summary, + child_summaries: ArrayVec, + child_trees: ArrayVec, { 2 * TREE_BASE }>, + }, + Leaf { + summary: T::Summary, + items: ArrayVec, + item_summaries: ArrayVec, + }, +} + +impl fmt::Debug for Node +where + T: Item + fmt::Debug, + T::Summary: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Node::Internal { + height, + summary, + child_summaries, + child_trees, + } => f + .debug_struct("Internal") + .field("height", height) + .field("summary", summary) + .field("child_summaries", child_summaries) + .field("child_trees", child_trees) + .finish(), + Node::Leaf { + summary, + items, + item_summaries, + } => f + .debug_struct("Leaf") + .field("summary", summary) + .field("items", items) + .field("item_summaries", item_summaries) + .finish(), + } + } +} + +impl Node { + fn is_leaf(&self) -> bool { + matches!(self, Node::Leaf { .. }) + } + + fn height(&self) -> u8 { + match self { + Node::Internal { height, .. } => *height, + Node::Leaf { .. } => 0, + } + } + + fn summary(&self) -> &T::Summary { + match self { + Node::Internal { summary, .. } => summary, + Node::Leaf { summary, .. } => summary, + } + } + + fn child_summaries(&self) -> &[T::Summary] { + match self { + Node::Internal { + child_summaries, .. + } => child_summaries.as_slice(), + Node::Leaf { item_summaries, .. } => item_summaries.as_slice(), + } + } + + fn child_trees(&self) -> &ArrayVec, { 2 * TREE_BASE }> { + match self { + Node::Internal { child_trees, .. } => child_trees, + Node::Leaf { .. } => panic!("Leaf nodes have no child trees"), + } + } + + fn items(&self) -> &ArrayVec { + match self { + Node::Leaf { items, .. } => items, + Node::Internal { .. } => panic!("Internal nodes have no items"), + } + } + + fn is_underflowing(&self) -> bool { + match self { + Node::Internal { child_trees, .. } => child_trees.len() < TREE_BASE, + Node::Leaf { items, .. } => items.len() < TREE_BASE, + } + } +} + +#[derive(Debug)] +pub enum Edit { + Insert(T), + Remove(T::Key), +} + +impl Edit { + fn key(&self) -> T::Key { + match self { + Edit::Insert(item) => item.key(), + Edit::Remove(key) => key.clone(), + } + } +} + +fn sum<'a, T, I>(iter: I, cx: T::Context<'_>) -> T +where + T: 'a + Summary, + I: Iterator, +{ + let mut sum = T::zero(cx); + for value in iter { + sum.add_summary(value, cx); + } + sum +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::{distr::StandardUniform, prelude::*}; + use std::cmp; + + #[ctor::ctor] + fn init_logger() { + zlog::init_test(); + } + + #[test] + fn test_extend_and_push_tree() { + let mut tree1 = SumTree::default(); + tree1.extend(0..20, ()); + + let mut tree2 = SumTree::default(); + tree2.extend(50..100, ()); + + tree1.append(tree2, ()); + assert_eq!(tree1.items(()), (0..20).chain(50..100).collect::>()); + } + + #[test] + fn test_random() { + let mut starting_seed = 0; + if let Ok(value) = std::env::var("SEED") { + starting_seed = value.parse().expect("invalid SEED variable"); + } + let mut num_iterations = 100; + if let Ok(value) = std::env::var("ITERATIONS") { + num_iterations = value.parse().expect("invalid ITERATIONS variable"); + } + let num_operations = std::env::var("OPERATIONS") + .map_or(5, |o| o.parse().expect("invalid OPERATIONS variable")); + + for seed in starting_seed..(starting_seed + num_iterations) { + eprintln!("seed = {}", seed); + let mut rng = StdRng::seed_from_u64(seed); + + let rng = &mut rng; + let mut tree = SumTree::::default(); + let count = rng.random_range(0..10); + if rng.random() { + tree.extend(rng.sample_iter(StandardUniform).take(count), ()); + } else { + let items = rng + .sample_iter(StandardUniform) + .take(count) + .collect::>(); + tree.par_extend(items, ()); + } + + for _ in 0..num_operations { + let splice_end = rng.random_range(0..tree.extent::(()).0 + 1); + let splice_start = rng.random_range(0..splice_end + 1); + let count = rng.random_range(0..10); + let tree_end = tree.extent::(()); + let new_items = rng + .sample_iter(StandardUniform) + .take(count) + .collect::>(); + + let mut reference_items = tree.items(()); + reference_items.splice(splice_start..splice_end, new_items.clone()); + + tree = { + let mut cursor = tree.cursor::(()); + let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right); + if rng.random() { + new_tree.extend(new_items, ()); + } else { + new_tree.par_extend(new_items, ()); + } + cursor.seek(&Count(splice_end), Bias::Right); + new_tree.append(cursor.slice(&tree_end, Bias::Right), ()); + new_tree + }; + + assert_eq!(tree.items(()), reference_items); + assert_eq!( + tree.iter().collect::>(), + tree.cursor::<()>(()).collect::>() + ); + + log::info!("tree items: {:?}", tree.items(())); + + let mut filter_cursor = + tree.filter::<_, Count>((), |summary| summary.contains_even); + let expected_filtered_items = tree + .items(()) + .into_iter() + .enumerate() + .filter(|(_, item)| (item & 1) == 0) + .collect::>(); + + let mut item_ix = if rng.random() { + filter_cursor.next(); + 0 + } else { + filter_cursor.prev(); + expected_filtered_items.len().saturating_sub(1) + }; + while item_ix < expected_filtered_items.len() { + log::info!("filter_cursor, item_ix: {}", item_ix); + let actual_item = filter_cursor.item().unwrap(); + let (reference_index, reference_item) = expected_filtered_items[item_ix]; + assert_eq!(actual_item, &reference_item); + assert_eq!(filter_cursor.start().0, reference_index); + log::info!("next"); + filter_cursor.next(); + item_ix += 1; + + while item_ix > 0 && rng.random_bool(0.2) { + log::info!("prev"); + filter_cursor.prev(); + item_ix -= 1; + + if item_ix == 0 && rng.random_bool(0.2) { + filter_cursor.prev(); + assert_eq!(filter_cursor.item(), None); + assert_eq!(filter_cursor.start().0, 0); + filter_cursor.next(); + } + } + } + assert_eq!(filter_cursor.item(), None); + + let mut before_start = false; + let mut cursor = tree.cursor::(()); + let start_pos = rng.random_range(0..=reference_items.len()); + cursor.seek(&Count(start_pos), Bias::Right); + let mut pos = rng.random_range(start_pos..=reference_items.len()); + cursor.seek_forward(&Count(pos), Bias::Right); + + for i in 0..10 { + assert_eq!(cursor.start().0, pos); + + if pos > 0 { + assert_eq!(cursor.prev_item().unwrap(), &reference_items[pos - 1]); + } else { + assert_eq!(cursor.prev_item(), None); + } + + if pos < reference_items.len() && !before_start { + assert_eq!(cursor.item().unwrap(), &reference_items[pos]); + } else { + assert_eq!(cursor.item(), None); + } + + if before_start { + assert_eq!(cursor.next_item(), reference_items.first()); + } else if pos + 1 < reference_items.len() { + assert_eq!(cursor.next_item().unwrap(), &reference_items[pos + 1]); + } else { + assert_eq!(cursor.next_item(), None); + } + + if i < 5 { + cursor.next(); + if pos < reference_items.len() { + pos += 1; + before_start = false; + } + } else { + cursor.prev(); + if pos == 0 { + before_start = true; + } + pos = pos.saturating_sub(1); + } + } + } + + for _ in 0..10 { + let end = rng.random_range(0..tree.extent::(()).0 + 1); + let start = rng.random_range(0..end + 1); + let start_bias = if rng.random() { + Bias::Left + } else { + Bias::Right + }; + let end_bias = if rng.random() { + Bias::Left + } else { + Bias::Right + }; + + let mut cursor = tree.cursor::(()); + cursor.seek(&Count(start), start_bias); + let slice = cursor.slice(&Count(end), end_bias); + + cursor.seek(&Count(start), start_bias); + let summary = cursor.summary::<_, Sum>(&Count(end), end_bias); + + assert_eq!(summary.0, slice.summary().sum); + } + } + } + + #[test] + fn test_cursor() { + // Empty tree + let tree = SumTree::::default(); + let mut cursor = tree.cursor::(()); + assert_eq!( + cursor.slice(&Count(0), Bias::Right).items(()), + Vec::::new() + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + cursor.prev(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + cursor.next(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + + // Single-element tree + let mut tree = SumTree::::default(); + tree.extend(vec![1], ()); + let mut cursor = tree.cursor::(()); + assert_eq!( + cursor.slice(&Count(0), Bias::Right).items(()), + Vec::::new() + ); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + + cursor.next(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 1); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 0); + + let mut cursor = tree.cursor::(()); + assert_eq!(cursor.slice(&Count(1), Bias::Right).items(()), [1]); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 1); + + cursor.seek(&Count(0), Bias::Right); + assert_eq!( + cursor + .slice(&tree.extent::(()), Bias::Right) + .items(()), + [1] + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 1); + + // Multiple-element tree + let mut tree = SumTree::default(); + tree.extend(vec![1, 2, 3, 4, 5, 6], ()); + let mut cursor = tree.cursor::(()); + + assert_eq!(cursor.slice(&Count(2), Bias::Right).items(()), [1, 2]); + assert_eq!(cursor.item(), Some(&3)); + assert_eq!(cursor.prev_item(), Some(&2)); + assert_eq!(cursor.next_item(), Some(&4)); + assert_eq!(cursor.start().sum, 3); + + cursor.next(); + assert_eq!(cursor.item(), Some(&4)); + assert_eq!(cursor.prev_item(), Some(&3)); + assert_eq!(cursor.next_item(), Some(&5)); + assert_eq!(cursor.start().sum, 6); + + cursor.next(); + assert_eq!(cursor.item(), Some(&5)); + assert_eq!(cursor.prev_item(), Some(&4)); + assert_eq!(cursor.next_item(), Some(&6)); + assert_eq!(cursor.start().sum, 10); + + cursor.next(); + assert_eq!(cursor.item(), Some(&6)); + assert_eq!(cursor.prev_item(), Some(&5)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 15); + + cursor.next(); + cursor.next(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&6)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 21); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&6)); + assert_eq!(cursor.prev_item(), Some(&5)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 15); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&5)); + assert_eq!(cursor.prev_item(), Some(&4)); + assert_eq!(cursor.next_item(), Some(&6)); + assert_eq!(cursor.start().sum, 10); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&4)); + assert_eq!(cursor.prev_item(), Some(&3)); + assert_eq!(cursor.next_item(), Some(&5)); + assert_eq!(cursor.start().sum, 6); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&3)); + assert_eq!(cursor.prev_item(), Some(&2)); + assert_eq!(cursor.next_item(), Some(&4)); + assert_eq!(cursor.start().sum, 3); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&2)); + assert_eq!(cursor.prev_item(), Some(&1)); + assert_eq!(cursor.next_item(), Some(&3)); + assert_eq!(cursor.start().sum, 1); + + cursor.prev(); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), Some(&2)); + assert_eq!(cursor.start().sum, 0); + + cursor.prev(); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), Some(&1)); + assert_eq!(cursor.start().sum, 0); + + cursor.next(); + assert_eq!(cursor.item(), Some(&1)); + assert_eq!(cursor.prev_item(), None); + assert_eq!(cursor.next_item(), Some(&2)); + assert_eq!(cursor.start().sum, 0); + + let mut cursor = tree.cursor::(()); + assert_eq!( + cursor + .slice(&tree.extent::(()), Bias::Right) + .items(()), + tree.items(()) + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&6)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 21); + + cursor.seek(&Count(3), Bias::Right); + assert_eq!( + cursor + .slice(&tree.extent::(()), Bias::Right) + .items(()), + [4, 5, 6] + ); + assert_eq!(cursor.item(), None); + assert_eq!(cursor.prev_item(), Some(&6)); + assert_eq!(cursor.next_item(), None); + assert_eq!(cursor.start().sum, 21); + + // Seeking can bias left or right + cursor.seek(&Count(1), Bias::Left); + assert_eq!(cursor.item(), Some(&1)); + cursor.seek(&Count(1), Bias::Right); + assert_eq!(cursor.item(), Some(&2)); + + // Slicing without resetting starts from where the cursor is parked at. + cursor.seek(&Count(1), Bias::Right); + assert_eq!(cursor.slice(&Count(3), Bias::Right).items(()), vec![2, 3]); + assert_eq!(cursor.slice(&Count(6), Bias::Left).items(()), vec![4, 5]); + assert_eq!(cursor.slice(&Count(6), Bias::Right).items(()), vec![6]); + } + + #[test] + fn test_edit() { + let mut tree = SumTree::::default(); + + let removed = tree.edit(vec![Edit::Insert(1), Edit::Insert(2), Edit::Insert(0)], ()); + assert_eq!(tree.items(()), vec![0, 1, 2]); + assert_eq!(removed, Vec::::new()); + assert_eq!(tree.get(&0, ()), Some(&0)); + assert_eq!(tree.get(&1, ()), Some(&1)); + assert_eq!(tree.get(&2, ()), Some(&2)); + assert_eq!(tree.get(&4, ()), None); + + let removed = tree.edit(vec![Edit::Insert(2), Edit::Insert(4), Edit::Remove(0)], ()); + assert_eq!(tree.items(()), vec![1, 2, 4]); + assert_eq!(removed, vec![0, 2]); + assert_eq!(tree.get(&0, ()), None); + assert_eq!(tree.get(&1, ()), Some(&1)); + assert_eq!(tree.get(&2, ()), Some(&2)); + assert_eq!(tree.get(&4, ()), Some(&4)); + } + + #[test] + fn test_from_iter() { + assert_eq!( + SumTree::from_iter(0..100, ()).items(()), + (0..100).collect::>() + ); + + // Ensure `from_iter` works correctly when the given iterator restarts + // after calling `next` if `None` was already returned. + let mut ix = 0; + let iterator = std::iter::from_fn(|| { + ix = (ix + 1) % 2; + if ix == 1 { Some(1) } else { None } + }); + assert_eq!(SumTree::from_iter(iterator, ()).items(()), vec![1]); + } + + #[derive(Clone, Default, Debug)] + pub struct IntegersSummary { + count: usize, + sum: usize, + contains_even: bool, + max: u8, + } + + #[derive(Ord, PartialOrd, Default, Eq, PartialEq, Clone, Debug)] + struct Count(usize); + + #[derive(Ord, PartialOrd, Default, Eq, PartialEq, Clone, Debug)] + struct Sum(usize); + + impl Item for u8 { + type Summary = IntegersSummary; + + fn summary(&self, _cx: ()) -> Self::Summary { + IntegersSummary { + count: 1, + sum: *self as usize, + contains_even: (*self & 1) == 0, + max: *self, + } + } + } + + impl KeyedItem for u8 { + type Key = u8; + + fn key(&self) -> Self::Key { + *self + } + } + + impl ContextLessSummary for IntegersSummary { + fn zero() -> Self { + Default::default() + } + + fn add_summary(&mut self, other: &Self) { + self.count += other.count; + self.sum += other.sum; + self.contains_even |= other.contains_even; + self.max = cmp::max(self.max, other.max); + } + } + + impl Dimension<'_, IntegersSummary> for u8 { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &IntegersSummary, _: ()) { + *self = summary.max; + } + } + + impl Dimension<'_, IntegersSummary> for Count { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &IntegersSummary, _: ()) { + self.0 += summary.count; + } + } + + impl SeekTarget<'_, IntegersSummary, IntegersSummary> for Count { + fn cmp(&self, cursor_location: &IntegersSummary, _: ()) -> Ordering { + self.0.cmp(&cursor_location.count) + } + } + + impl Dimension<'_, IntegersSummary> for Sum { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &IntegersSummary, _: ()) { + self.0 += summary.sum; + } + } +} diff --git a/third_party/gpui_support/zed-sum-tree/src/tree_map.rs b/third_party/gpui_support/zed-sum-tree/src/tree_map.rs new file mode 100644 index 0000000..6263040 --- /dev/null +++ b/third_party/gpui_support/zed-sum-tree/src/tree_map.rs @@ -0,0 +1,520 @@ +use std::{cmp::Ordering, fmt::Debug}; + +use crate::{Bias, ContextLessSummary, Dimension, Edit, Item, KeyedItem, SeekTarget, SumTree}; + +/// A cheaply-cloneable ordered map based on a [SumTree](crate::SumTree). +#[derive(Clone, PartialEq, Eq)] +pub struct TreeMap(SumTree>) +where + K: Clone + Ord, + V: Clone; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MapEntry { + key: K, + value: V, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct MapKey(Option); + +impl Default for MapKey { + fn default() -> Self { + Self(None) + } +} + +#[derive(Clone, Debug)] +pub struct MapKeyRef<'a, K>(Option<&'a K>); + +impl Default for MapKeyRef<'_, K> { + fn default() -> Self { + Self(None) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TreeSet(TreeMap) +where + K: Clone + Ord; + +impl TreeMap { + pub fn from_ordered_entries(entries: impl IntoIterator) -> Self { + let tree = SumTree::from_iter( + entries + .into_iter() + .map(|(key, value)| MapEntry { key, value }), + (), + ); + Self(tree) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn get(&self, key: &K) -> Option<&V> { + let mut cursor = self.0.cursor::>(()); + cursor.seek(&MapKeyRef(Some(key)), Bias::Left); + if let Some(item) = cursor.item() { + if Some(key) == item.key().0.as_ref() { + Some(&item.value) + } else { + None + } + } else { + None + } + } + + pub fn insert(&mut self, key: K, value: V) { + self.0.insert_or_replace(MapEntry { key, value }, ()); + } + + pub fn extend(&mut self, iter: impl IntoIterator) { + let edits: Vec<_> = iter + .into_iter() + .map(|(key, value)| Edit::Insert(MapEntry { key, value })) + .collect(); + self.0.edit(edits, ()); + } + + pub fn clear(&mut self) { + self.0 = SumTree::default(); + } + + pub fn remove(&mut self, key: &K) -> Option { + let mut removed = None; + let mut cursor = self.0.cursor::>(()); + let key = MapKeyRef(Some(key)); + let mut new_tree = cursor.slice(&key, Bias::Left); + if key.cmp(&cursor.end(), ()) == Ordering::Equal { + removed = Some(cursor.item().unwrap().value.clone()); + cursor.next(); + } + new_tree.append(cursor.suffix(), ()); + drop(cursor); + self.0 = new_tree; + removed + } + + pub fn remove_range(&mut self, start: &impl MapSeekTarget, end: &impl MapSeekTarget) { + let start = MapSeekTargetAdaptor(start); + let end = MapSeekTargetAdaptor(end); + let mut cursor = self.0.cursor::>(()); + let mut new_tree = cursor.slice(&start, Bias::Left); + cursor.seek(&end, Bias::Left); + new_tree.append(cursor.suffix(), ()); + drop(cursor); + self.0 = new_tree; + } + + /// Returns the key-value pair with the greatest key less than or equal to the given key. + pub fn closest(&self, key: &K) -> Option<(&K, &V)> { + let mut cursor = self.0.cursor::>(()); + let key = MapKeyRef(Some(key)); + cursor.seek(&key, Bias::Right); + cursor.prev(); + cursor.item().map(|item| (&item.key, &item.value)) + } + + pub fn iter_from<'a>(&'a self, from: &K) -> impl Iterator + 'a { + let mut cursor = self.0.cursor::>(()); + let from_key = MapKeyRef(Some(from)); + cursor.seek(&from_key, Bias::Left); + + cursor.map(|map_entry| (&map_entry.key, &map_entry.value)) + } + + pub fn update(&mut self, key: &K, f: F) -> Option + where + F: FnOnce(&mut V) -> T, + { + let mut cursor = self.0.cursor::>(()); + let key = MapKeyRef(Some(key)); + let mut new_tree = cursor.slice(&key, Bias::Left); + let mut result = None; + if key.cmp(&cursor.end(), ()) == Ordering::Equal { + let mut updated = cursor.item().unwrap().clone(); + result = Some(f(&mut updated.value)); + new_tree.push(updated, ()); + cursor.next(); + } + new_tree.append(cursor.suffix(), ()); + drop(cursor); + self.0 = new_tree; + result + } + + pub fn retain bool>(&mut self, mut predicate: F) { + let mut new_map = SumTree::>::default(); + + let mut cursor = self.0.cursor::>(()); + cursor.next(); + while let Some(item) = cursor.item() { + if predicate(&item.key, &item.value) { + new_map.push(item.clone(), ()); + } + cursor.next(); + } + drop(cursor); + + self.0 = new_map; + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().map(|entry| (&entry.key, &entry.value)) + } + + pub fn values(&self) -> impl Iterator + '_ { + self.0.iter().map(|entry| &entry.value) + } + + pub fn first(&self) -> Option<(&K, &V)> { + self.0.first().map(|entry| (&entry.key, &entry.value)) + } + + pub fn last(&self) -> Option<(&K, &V)> { + self.0.last().map(|entry| (&entry.key, &entry.value)) + } + + pub fn insert_tree(&mut self, other: TreeMap) { + let edits = other + .iter() + .map(|(key, value)| { + Edit::Insert(MapEntry { + key: key.to_owned(), + value: value.to_owned(), + }) + }) + .collect(); + + self.0.edit(edits, ()); + } +} + +impl Debug for TreeMap +where + K: Clone + Debug + Ord, + V: Clone + Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +#[derive(Debug)] +struct MapSeekTargetAdaptor<'a, T>(&'a T); + +impl<'a, K: Clone + Ord, T: MapSeekTarget> SeekTarget<'a, MapKey, MapKeyRef<'a, K>> + for MapSeekTargetAdaptor<'_, T> +{ + fn cmp(&self, cursor_location: &MapKeyRef, _: ()) -> Ordering { + if let Some(key) = &cursor_location.0 { + MapSeekTarget::cmp_cursor(self.0, key) + } else { + Ordering::Greater + } + } +} + +pub trait MapSeekTarget { + fn cmp_cursor(&self, cursor_location: &K) -> Ordering; +} + +impl MapSeekTarget for K { + fn cmp_cursor(&self, cursor_location: &K) -> Ordering { + self.cmp(cursor_location) + } +} + +impl Default for TreeMap +where + K: Clone + Ord, + V: Clone, +{ + fn default() -> Self { + Self(Default::default()) + } +} + +impl Item for MapEntry +where + K: Clone + Ord, + V: Clone, +{ + type Summary = MapKey; + + fn summary(&self, _cx: ()) -> Self::Summary { + self.key() + } +} + +impl KeyedItem for MapEntry +where + K: Clone + Ord, + V: Clone, +{ + type Key = MapKey; + + fn key(&self) -> Self::Key { + MapKey(Some(self.key.clone())) + } +} + +impl ContextLessSummary for MapKey +where + K: Clone, +{ + fn zero() -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &Self) { + *self = summary.clone() + } +} + +impl<'a, K> Dimension<'a, MapKey> for MapKeyRef<'a, K> +where + K: Clone + Ord, +{ + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &'a MapKey, _: ()) { + self.0 = summary.0.as_ref(); + } +} + +impl<'a, K> SeekTarget<'a, MapKey, MapKeyRef<'a, K>> for MapKeyRef<'_, K> +where + K: Clone + Ord, +{ + fn cmp(&self, cursor_location: &MapKeyRef, _: ()) -> Ordering { + Ord::cmp(&self.0, &cursor_location.0) + } +} + +impl Default for TreeSet +where + K: Clone + Ord, +{ + fn default() -> Self { + Self(Default::default()) + } +} + +impl TreeSet +where + K: Clone + Ord, +{ + pub fn from_ordered_entries(entries: impl IntoIterator) -> Self { + Self(TreeMap::from_ordered_entries( + entries.into_iter().map(|key| (key, ())), + )) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn insert(&mut self, key: K) { + self.0.insert(key, ()); + } + + pub fn remove(&mut self, key: &K) -> bool { + self.0.remove(key).is_some() + } + + pub fn extend(&mut self, iter: impl IntoIterator) { + self.0.extend(iter.into_iter().map(|key| (key, ()))); + } + + pub fn contains(&self, key: &K) -> bool { + self.0.get(key).is_some() + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().map(|(k, _)| k) + } + + pub fn iter_from<'a>(&'a self, key: &K) -> impl Iterator + 'a { + self.0.iter_from(key).map(move |(k, _)| k) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic() { + let mut map = TreeMap::default(); + assert_eq!(map.iter().collect::>(), vec![]); + + map.insert(3, "c"); + assert_eq!(map.get(&3), Some(&"c")); + assert_eq!(map.iter().collect::>(), vec![(&3, &"c")]); + + map.insert(1, "a"); + assert_eq!(map.get(&1), Some(&"a")); + assert_eq!(map.iter().collect::>(), vec![(&1, &"a"), (&3, &"c")]); + + map.insert(2, "b"); + assert_eq!(map.get(&2), Some(&"b")); + assert_eq!(map.get(&1), Some(&"a")); + assert_eq!(map.get(&3), Some(&"c")); + assert_eq!( + map.iter().collect::>(), + vec![(&1, &"a"), (&2, &"b"), (&3, &"c")] + ); + + assert_eq!(map.closest(&0), None); + assert_eq!(map.closest(&1), Some((&1, &"a"))); + assert_eq!(map.closest(&10), Some((&3, &"c"))); + + map.remove(&2); + assert_eq!(map.get(&2), None); + assert_eq!(map.iter().collect::>(), vec![(&1, &"a"), (&3, &"c")]); + + assert_eq!(map.closest(&2), Some((&1, &"a"))); + + map.remove(&3); + assert_eq!(map.get(&3), None); + assert_eq!(map.iter().collect::>(), vec![(&1, &"a")]); + + map.remove(&1); + assert_eq!(map.get(&1), None); + assert_eq!(map.iter().collect::>(), vec![]); + + map.insert(4, "d"); + map.insert(5, "e"); + map.insert(6, "f"); + map.retain(|key, _| *key % 2 == 0); + assert_eq!(map.iter().collect::>(), vec![(&4, &"d"), (&6, &"f")]); + } + + #[test] + fn test_iter_from() { + let mut map = TreeMap::default(); + + map.insert("a", 1); + map.insert("b", 2); + map.insert("baa", 3); + map.insert("baaab", 4); + map.insert("c", 5); + + let result = map + .iter_from(&"ba") + .take_while(|(key, _)| key.starts_with("ba")) + .collect::>(); + + assert_eq!(result.len(), 2); + assert!(result.iter().any(|(k, _)| k == &&"baa")); + assert!(result.iter().any(|(k, _)| k == &&"baaab")); + + let result = map + .iter_from(&"c") + .take_while(|(key, _)| key.starts_with("c")) + .collect::>(); + + assert_eq!(result.len(), 1); + assert!(result.iter().any(|(k, _)| k == &&"c")); + } + + #[test] + fn test_insert_tree() { + let mut map = TreeMap::default(); + map.insert("a", 1); + map.insert("b", 2); + map.insert("c", 3); + + let mut other = TreeMap::default(); + other.insert("a", 2); + other.insert("b", 2); + other.insert("d", 4); + + map.insert_tree(other); + + assert_eq!(map.iter().count(), 4); + assert_eq!(map.get(&"a"), Some(&2)); + assert_eq!(map.get(&"b"), Some(&2)); + assert_eq!(map.get(&"c"), Some(&3)); + assert_eq!(map.get(&"d"), Some(&4)); + } + + #[test] + fn test_extend() { + let mut map = TreeMap::default(); + map.insert("a", 1); + map.insert("b", 2); + map.insert("c", 3); + map.extend([("a", 2), ("b", 2), ("d", 4)]); + assert_eq!(map.iter().count(), 4); + assert_eq!(map.get(&"a"), Some(&2)); + assert_eq!(map.get(&"b"), Some(&2)); + assert_eq!(map.get(&"c"), Some(&3)); + assert_eq!(map.get(&"d"), Some(&4)); + } + + #[test] + fn test_remove_between_and_path_successor() { + use std::path::{Path, PathBuf}; + + #[derive(Debug)] + pub struct PathDescendants<'a>(&'a Path); + + impl MapSeekTarget for PathDescendants<'_> { + fn cmp_cursor(&self, key: &PathBuf) -> Ordering { + if key.starts_with(self.0) { + Ordering::Greater + } else { + self.0.cmp(key) + } + } + } + + let mut map = TreeMap::default(); + + map.insert(PathBuf::from("a"), 1); + map.insert(PathBuf::from("a/a"), 1); + map.insert(PathBuf::from("b"), 2); + map.insert(PathBuf::from("b/a/a"), 3); + map.insert(PathBuf::from("b/a/a/a/b"), 4); + map.insert(PathBuf::from("c"), 5); + map.insert(PathBuf::from("c/a"), 6); + + map.remove_range( + &PathBuf::from("b/a"), + &PathDescendants(&PathBuf::from("b/a")), + ); + + assert_eq!(map.get(&PathBuf::from("a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("a/a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("b")), Some(&2)); + assert_eq!(map.get(&PathBuf::from("b/a/a")), None); + assert_eq!(map.get(&PathBuf::from("b/a/a/a/b")), None); + assert_eq!(map.get(&PathBuf::from("c")), Some(&5)); + assert_eq!(map.get(&PathBuf::from("c/a")), Some(&6)); + + map.remove_range(&PathBuf::from("c"), &PathDescendants(&PathBuf::from("c"))); + + assert_eq!(map.get(&PathBuf::from("a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("a/a")), Some(&1)); + assert_eq!(map.get(&PathBuf::from("b")), Some(&2)); + assert_eq!(map.get(&PathBuf::from("c")), None); + assert_eq!(map.get(&PathBuf::from("c/a")), None); + + map.remove_range(&PathBuf::from("a"), &PathDescendants(&PathBuf::from("a"))); + + assert_eq!(map.get(&PathBuf::from("a")), None); + assert_eq!(map.get(&PathBuf::from("a/a")), None); + assert_eq!(map.get(&PathBuf::from("b")), Some(&2)); + + map.remove_range(&PathBuf::from("b"), &PathDescendants(&PathBuf::from("b"))); + + assert_eq!(map.get(&PathBuf::from("b")), None); + } +}