pax_global_header00006660000000000000000000000064146570172660014530gustar00rootroot0000000000000052 comment=dd0b3a4e3944e4dfefa8343a65d832ae7586de7a smol-2.0.1/000077500000000000000000000000001465701726600125025ustar00rootroot00000000000000smol-2.0.1/.github/000077500000000000000000000000001465701726600140425ustar00rootroot00000000000000smol-2.0.1/.github/dependabot.yml000066400000000000000000000002331465701726600166700ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: cargo directory: / schedule: interval: weekly commit-message: prefix: '' labels: [] smol-2.0.1/.github/workflows/000077500000000000000000000000001465701726600160775ustar00rootroot00000000000000smol-2.0.1/.github/workflows/ci.yml000066400000000000000000000042401465701726600172150ustar00rootroot00000000000000name: CI permissions: contents: read on: pull_request: push: branches: - master schedule: - cron: '0 2 * * 0' env: CARGO_INCREMENTAL: 0 CARGO_NET_GIT_FETCH_WITH_CLI: true CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 RUSTFLAGS: -D warnings RUSTDOCFLAGS: -D warnings RUSTUP_MAX_RETRIES: 10 defaults: run: shell: bash jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] rust: [nightly, beta, stable] steps: - uses: actions/checkout@v4 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} - run: cargo build --all --all-features --all-targets - name: Run cargo check (without dev-dependencies to catch missing feature flags) if: startsWith(matrix.rust, 'nightly') run: cargo check -Z features=dev_dep - run: cargo test msrv: runs-on: ubuntu-latest strategy: matrix: # When updating this, the reminder to update the minimum supported # Rust version in Cargo.toml and README.md. rust: ['1.63'] steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - run: cargo build clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - run: cargo clippy --all-features --all-targets fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - run: cargo fmt --all --check security_audit: permissions: checks: write contents: read issues: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # https://github.com/rustsec/audit-check/issues/2 - uses: rustsec/audit-check@master with: token: ${{ secrets.GITHUB_TOKEN }} smol-2.0.1/.github/workflows/release.yml000066400000000000000000000006411465701726600202430ustar00rootroot00000000000000name: Release permissions: contents: write on: push: tags: - v[0-9]+.* jobs: create-release: if: github.repository_owner == 'smol-rs' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: taiki-e/create-gh-release-action@v1 with: changelog: CHANGELOG.md branch: master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} smol-2.0.1/.gitignore000066400000000000000000000000231465701726600144650ustar00rootroot00000000000000target/ Cargo.lock smol-2.0.1/CHANGELOG.md000066400000000000000000000065571465701726600143300ustar00rootroot00000000000000# Version 2.0.1 - Add a mention to the documentation of `smol::spawn` that tasks spawned with this function don't have their destructors called when the program ends. (#312) # Version 2.0.0 - **Breaking:** Bump subcrates to their newest major versions. (#277, #280, #281, #282, #283) - Run the `async-process` driver on the executor. (#284) # Version 1.3.0 - Remove the dependency on the `once_cell` crate to restore the MSRV. (#241) # Version 1.2.5 - Bump version for docs.rs to pick up latest dependencies. # Version 1.2.4 - Update dependencies. # Version 1.2.3 - Bump version for docs.rs to pick up latest dependencies. # Version 1.2.2 - Bump version for docs.rs to pick up latest dependencies. # Version 1.2.1 - Temporarily downgrade `async-executor`. # Version 1.2.0 - Update all dependencies. # Version 1.1.0 - Update `async-executor`. # Version 1.0.1 - Update dependencies. # Version 1.0.0 - Stabilize. # Version 0.4.2 - Update dependencies. # Version 0.4.1 - Bring back `SMOL_THREADS`. # Version 0.4.0 - Add `process`, `fs`, `net`, `lock`, `channel` modules. - Update all dependencies - Remove `smol::run()`. # Version 0.3.3 - Add `block_on()`. - Use `SMOL_THREADS` environment variable. # Version 0.3.2 - Reexport `FutureExt`. # Version 0.3.1 - Fix some typos in docs. # Version 0.3.0 - Reexport `futures-lite`, `blocking`, `async-executor`. - Re-introduce `smol::run()`. # Version 0.2.0 - Split `smol` into `async-io`, `blocking`, and `multitask`. - Big breaking change - there is now only one type `Task`. # Version 0.1.18 - Support Rust 1.39.0 # Version 0.1.17 - Support more platforms by changing `AtomicU64` to `AtomicUsize`. - Remove `IoEvent` and simplify reactor notification. # Version 0.1.16 - Add `Async::readable()` and `Async::writable()`. # Version 0.1.15 - Fix wakeups lost inside the executor. - Fix a fairness issue in the executor. # Version 0.1.14 - Clear the flag after every call to `react()`. # Version 0.1.13 - Fix deadlocks caused by lost wakeups. - Refactor the executor. # Version 0.1.12 - Fix a bug in `Async::::recv()`. # Version 0.1.11 - Update `wepoll-binding`. - Reduce dependencies. - Replace `nix` with `libc`. - Set minimum required `tokio` version to 0.2. # Version 0.1.10 - Fix incorrectly reported error kind when connecting fails. # Version 0.1.9 - Switch to oneshot-style notifications on all platforms. - Fix a bug that caused 100% CPU usage on Windows. - Deprecate `Async::with()` and `Async::with_mut()`. - Add `Async::read_with()`, `Async::read_with_mut()`, `Async::write_with()`, and `Async::write_with_mut()`. - Fix a bug where eventfd was not closed. # Version 0.1.8 - Revert the use of `blocking` crate. # Version 0.1.7 - Update `blocking` to `0.4.2`. - Make `Task::blocking()` work without `run()`. # Version 0.1.6 - Fix a deadlock by always re-registering `IoEvent`. # Version 0.1.5 - Use `blocking` crate for blocking I/O. - Fix a re-registration bug when in oneshot mode. - Use eventfd on Linux. - More tests. - Fix timeout rounding error in epoll/wepoll. # Version 0.1.4 - Fix a bug in UDS async connect # Version 0.1.3 - Fix the writability check in async connect - More comments and documentation - Better security advice on certificates # Version 0.1.2 - Improved internal docs, fixed typos, and more comments # Version 0.1.1 - Upgrade dependencies # Version 0.1.0 - Initial release smol-2.0.1/CODE_OF_CONDUCT.md000066400000000000000000000122011465701726600152750ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [stjepang@gmail.com](mailto:stjepang@gmail.com). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. smol-2.0.1/Cargo.toml000066400000000000000000000031411465701726600144310ustar00rootroot00000000000000[package] name = "smol" # When publishing a new version: # - Update CHANGELOG.md # - Create "v2.x.y" git tag version = "2.0.1" authors = ["Stjepan Glavina "] edition = "2018" rust-version = "1.63" description = "A small and fast async runtime" license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/smol" keywords = ["async", "await", "future", "io", "networking"] categories = ["asynchronous", "concurrency", "network-programming"] exclude = ["/.*"] [dependencies] async-channel = "2.0.0" async-executor = "1.5.0" async-fs = "2.0.0" async-io = "2.1.0" async-lock = "3.0.0" async-net = "2.0.0" blocking = "1.3.0" futures-lite = "2.0.0" [target.'cfg(not(target_os = "espidf"))'.dependencies] async-process = "2.0.0" [dev-dependencies] anyhow = "1" async-dup = "1" async-h1 = "2" async-native-tls = "0.5" async-tungstenite = { version = "0.27", features = ["async-native-tls"] } ctrlc = "3" doc-comment = "0.3" futures = "0.3" http = "1.1" http-body-util = "0.1.0" http-types = "2" hyper = { version = "1.0", default-features = false, features = ["client", "http1", "server"] } macro_rules_attribute = "0.2.0" native-tls = "0.2" scraper = "0.20" signal-hook = "0.3" smol-hyper = "0.1.0" smol-macros = "0.1.0" surf = { version = "2", default-features = false, features = ["h1-client"] } tempfile = "3" tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } url = "2" [target.'cfg(target_os = "linux")'.dev-dependencies] inotify = { version = "0.10", default-features = false } rustix = "0.38" timerfd = "1" [target.'cfg(windows)'.dev-dependencies] uds_windows = "1" smol-2.0.1/LICENSE-APACHE000066400000000000000000000251371465701726600144360ustar00rootroot00000000000000 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. smol-2.0.1/LICENSE-MIT000066400000000000000000000017771465701726600141520ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. smol-2.0.1/README.md000066400000000000000000000114561465701726600137700ustar00rootroot00000000000000# smol [![Build](https://github.com/smol-rs/smol/actions/workflows/ci.yml/badge.svg)]( https://github.com/smol-rs/smol/actions) [![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( https://github.com/smol-rs/smol) [![Cargo](https://img.shields.io/crates/v/smol.svg)]( https://crates.io/crates/smol) [![Documentation](https://docs.rs/smol/badge.svg)]( https://docs.rs/smol) [![Chat](https://img.shields.io/matrix/smol-rs%3Amatrix.org)]( https://matrix.to/#/#smol-rs:matrix.org) kitty A small and fast async runtime. This crate simply re-exports other smaller async crates (see the source). To use tokio-based libraries with smol, apply the [`async-compat`] adapter to futures and I/O types. ## Examples Connect to an HTTP website, make a GET request, and pipe the response to the standard output: ```rust,no_run use smol::{io, net, prelude::*, Unblock}; fn main() -> io::Result<()> { smol::block_on(async { let mut stream = net::TcpStream::connect("example.com:80").await?; let req = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"; stream.write_all(req).await?; let mut stdout = Unblock::new(std::io::stdout()); io::copy(stream, &mut stdout).await?; Ok(()) }) } ``` There's a lot more in the [examples] directory. [`async-compat`]: https://docs.rs/async-compat [examples]: https://github.com/smol-rs/smol/tree/master/examples [get-request]: https://github.com/smol-rs/smol/blob/master/examples/get-request.rs ## Subcrates - [async-channel] - Multi-producer multi-consumer channels - [async-executor] - Composable async executors - [async-fs] - Async filesystem primitives - [async-io] - Async adapter for I/O types, also timers - [async-lock] - Async locks (barrier, mutex, reader-writer lock, semaphore) - [async-net] - Async networking primitives (TCP/UDP/Unix) - [async-process] - Async interface for working with processes - [async-task] - Task abstraction for building executors - [blocking] - A thread pool for blocking I/O - [futures-lite] - A lighter fork of [futures] - [polling] - Portable interface to epoll, kqueue, event ports, and wepoll [async-io]: https://github.com/smol-rs/async-io [polling]: https://github.com/smol-rs/polling [nb-connect]: https://github.com/smol-rs/nb-connect [async-executor]: https://github.com/smol-rs/async-executor [async-task]: https://github.com/smol-rs/async-task [blocking]: https://github.com/smol-rs/blocking [futures-lite]: https://github.com/smol-rs/futures-lite [smol]: https://github.com/smol-rs/smol [async-net]: https://github.com/smol-rs/async-net [async-process]: https://github.com/smol-rs/async-process [async-fs]: https://github.com/smol-rs/async-fs [async-channel]: https://github.com/smol-rs/async-channel [concurrent-queue]: https://github.com/smol-rs/concurrent-queue [event-listener]: https://github.com/smol-rs/event-listener [async-lock]: https://github.com/smol-rs/async-lock [fastrand]: https://github.com/smol-rs/fastrand [futures]: https://github.com/rust-lang/futures-rs ## TLS certificate Some code examples are using TLS for authentication. The repository contains a self-signed certificate usable for testing, but it should **not** be used for real-world scenarios. Browsers and tools like curl will show this certificate as insecure. In browsers, accept the security prompt or use `curl -k` on the command line to bypass security warnings. The certificate file was generated using [minica](https://github.com/jsha/minica) and [openssl](https://www.openssl.org/): ```text minica --domains localhost -ip-addresses 127.0.0.1 -ca-cert certificate.pem openssl pkcs12 -export -out identity.pfx -inkey localhost/key.pem -in localhost/cert.pem ``` Another useful tool for making certificates is [mkcert]. [mkcert]: https://github.com/FiloSottile/mkcert ## MSRV Policy The Minimum Supported Rust Version (MSRV) of this crate is **1.63**. As a **tentative** policy, the MSRV will not advance past the [current Rust version provided by Debian Stable](https://packages.debian.org/stable/rust/rustc). At the time of writing, this version of Rust is *1.63*. However, the MSRV may be advanced further in the event of a major ecosystem shift or a security vulnerability. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. #### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. smol-2.0.1/assets/000077500000000000000000000000001465701726600140045ustar00rootroot00000000000000smol-2.0.1/assets/images/000077500000000000000000000000001465701726600152515ustar00rootroot00000000000000smol-2.0.1/assets/images/logo_fullsize.png000066400000000000000000003367151465701726600206530ustar00rootroot00000000000000PNG  IHDRyy &sRGBeXIfMM*S pHYsMMʍ/riTXtXML:com.adobe.xmp smolrskity 132 132 2 2 smolrskity Dے@IDATxǎeKgGkᡮJUUFDD 8cp֯ S4QAeDfe޼7tVG ǎo?#<"܏y|+۶ޙb2u8#8#ٙpGpGp#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#8#83|>$GpGp78#8 "$o_pGp'y 8#8#08ɛCrGpGI#8# NfGpGp߀#|R2j_>sqGpO#87@ބ#8pw#\ tá*ejz-Gpn'y77[LzMNgw//?DFCAqraf)#%%S,AlKCV[[sy54)Hac{3tk5]Ny ~ EOJ3lg9BIBڟA x^?DoNͦԻܰJ( .y5HuDRw hF+!i#)~E#{w.#8 'yuxgۃ@Bz [ߓ]ˢjC&r ˝R̵ (j?HJTrBS{e=<׉%&pd/#8?w;3-4ByA4!sjFռԹ܃P{:<[[ P-.,_Qx={^l$KE{;9)yy{2Tg0(ӭGpGIއcw:3 Uȝ|W*xe@ 2VL>X kK]߇Ժ|R(+"zt+w[T;)x=-T "Cre8#$ >vpf "hsox޼ ŃCJ4ΓJR %aW}%~7TVWC]l/_D<O> EdKO//HB[͐W*=Kw)weuʮW uG"usrđ=H[Q.)WFw[?#݁]t>)RLStkUso_#1Û|~0+%qE#w){X[!yȟ[OB^հ872Bʺ Sk ooۡ(U?aNu(gD YQً#8pwmG`v0( YE]U JZhhEWL(:ˊH^> KkɌ[7(ϞV÷\E֪^MJ *9̺2Uߋ#8pw}G`& "+rJW鴢%44Z+aQ[Ey אY&*ѳ:چ+"+wfݽ}v2UEnA&XLO>Rٰd"ƮjdjW˛jL|s>G8xہ*ܠ"b?#|&}&1AUMr,%@&Ur7G/H+bB./?9V 9Q "w߮%"+ܗ؂s/UE5౤6і'ig2o Gpn7Nn97HVAL*;;' 2dD֖d2 aAꛥHQd ˗dc9fK"i@:*F0uѡddUtj2Y]coA*Teݓ)ZvU.DyqGN򮆓rn/*%(wM ѭ%XDk 4!$2 sE!˷vEa1gKm6|XC;=9[ E|8e8ɻ !FN%Ŭs$*Ҥ@X)RdN]Vq>t GsP0L;-P"Y丩!]!FbW(m+o@:Cqk /#8#oy9F^MTc#{SCfIx6O&Ԓtu5<זdl^fXLIEV"sj,U>d~%bRHW'7_T= ~6<̿=m(ueBΆNy*8`;ǎ#87pnh*;7j]Q*Xe<ҥ|Q.@~y8)+*XW1Vd*%l$gzͭp(k_f޶9|S7&{p ldؔ;g3d7engb,>Gw+_w^!>% i<"J<6$br4PJ|8JPyE*!"{<RcR\@I%$'D֞Mh A"uEe^n-INKj(yZUuGo8ɻoo $7B6?3Q*Jԉם_Kfx:OE"I]D S4/ PN:F~?EDHWINfmE/GGRB15ݯu8BW牮ͫR0pG|䝏_qn4UY,Z\$tӄx'B(Q>o&4G^M؎H`I>o5ƃa%E J"xb4A}"U#߾7kC-'G8{+БKBiIםWRG!ތ#p4!Kz'u`$6҄.KMc˜kюdW'eaJSd 3(T̳cKJR`2"IEDNOcpgNdRA/ [6گX۴rɜD$z#[st瑈hW ]͛qGNNA'D`j ">AFmrX;Q*,KBYH WPEYۇS0b&% e RGfVc-d,2<~xxqo޿M!XThʍMoyP26Eu.77KJ"pڿjwG} z~#S<4C$IYz@d^}=+',h!iUx,V>'g}aaI[̲K'OD~N=7u L˺"@d_ ys>@Pd ψ (4PL|"ͷ#Sþ9cĂ6 ~$wVcvx ml."jsݞ8,ܷ%5j2V8 ؼSM35WZb}UBW}"n 0v.KGQİq[w) ZZj^UIS`;#pZN;80#%%yrT ާ*<% 'R*{reeD)JY"e(hZm QkKC9\VVK>zb5ς/3ZacU^]c c% t~p{{ 8ZqFL2US+3beH^O9E.\P0VIsY+D,RĤ`P0ٶ&#2&bڃ%%jS7pABKo^$(RS+b( 57g{DѣEQ |hϸx/kds#z}<8pSQEE7d1~,ypGW=G1KLB $ETkŒQT!U]'#QF^T8mK 3-C5pI#b߸_uH]+ZL=Jذ4 |;"%S Q}f!=H/muv)y=''t_{qGHp_#0FT9;O1;H؜,XkNQ2iB6u)9-cQDN)eEEVCYx5-TJzkqWtJҁڰ`3sC0 C$nn.G#+`p* W{YUEq\:ٗ,D wt*&Z[\z|0+ TV5ih$niO^W,c M ']1"?-}CFbXZ\ ؕ^^Jlߔ_qG $޼jhw_,Cer=KSs7q\T%ݩJAtLc{0e,yD@ f&օ׷)T-dӳXNrI5ZE%,/?9*&G{AR NșE1VlG8/,+J_e%E$/#8#ȸ 8 o蔑+XrUpNi'>0pvTvEb9>QF9x!YY.\ȟ1Ai! ^^䩤"KC:SOU/_5աY><Lhad:13}.tKl% u7񧃠T  ýV?E 7>S`)N,E$(yLɠ*]~f_q; Dhg 3/UD s B1I| Druz4;ybh.%cfٓ밫)#@7!K%x7<笿ά kjI!Ѻ*%G/X %6#8 'ymXpbēLFsDJ8FWJpCK-ZzQ +Wm#vy%P(V-~>7GڒDr' h!H"ǽ%vi7* %{Vu dLbShb]K5eDkyK kfիCVvDzzqG#$w~qbs9~M.:ncJvAbF0zr )w#m ܦvY?]twC2Z'u*E@M JeU+ŐU: nHޚVպ"B6 )nmp9Qi<|sˉ%$BkL#p4.MtTK?x-dQ$ADBh3y{BNwG!$q$8gLr<7&bԤ::Ltm 'FN5TVYY[NJθM u|2&Liaa鄑%L7NtN QCE0 <'G;;zgʝNA2VD RG@NT Pݜ[[oGfYi}υm=Y]FѨe22HsBs@q.׮ZgmlRhGߠ49YA\'؞~<9mQJgGJipWH#7ݷ7~$- H%n#03=ӡLi;b> ۛiI ωN1œ6H7>$Bĥ:0+I9Ng>xݝPmEXKj35P ܁Lf%y +2ÓG5DBUd] )۸41ܛ>y~7V_@_UUʣX=Oٻ~R`M"!D(Cb]߲_qG`vp7G6&(+?6y|F\RUQ2Ș5#r!1j/iL?Ť&qtqQN=}iĮkks/d: 2Wꄒ˷.gjݚHN]Ĭ#SbG ^Alо̲%d/+K"B,5D-5Af|qcLO1M{)-] ?Iԯx F"^EIC 8[;=Gp Nʛl)bDًmP<xMA'm掶C?'"ȊGa9mzb$ahe\}EzXڒ"xYmä#nqNiPD$#[PL 4>gISjiŹtxOzcf\}#DB\R. ̏*@;tyF#%j/o0/@YILAU8ݐqG!$SCA2 y߼ /^M+E: wAj5Oj&[H)_Zu/2_eo׈Y1/zЖYO2ߐI̽dM*#;17.#q]$Jڕ.'$Ɣ"f_M B K0h®f5~;no\FDn p-ಆ֔]J tu;alQR䮰j ,[`ҟ1.x;$F>vx'IXŠgWh:j(GG-S u-!UɌݙl18awPOߨGp;N0&xVUY>TMSXUQ_ x>Jk*3蕦C3,(/YFfȶH훐I"O\yOyݤFa-j >fGM=weud DnհZzs)H؈m53!q'^zf]̋]W;^; nAExQxs)\x^ðѕ̒(Z,E:j4궦+ ?*]W2+;o)4#D4I&&8Rɧujf5g!=~.3:|#ɳ '=JF~{#8 'y|hC0 TD<` L1 `հ667k4U M$HDX+#>R\08EP4== 8ij}U|PW<` CG{jLVd@G,ۅס&bW/YM>h+W"v~6}'|#'VKѷ"L5ILdvc]eaÏPr+:H~اp2 L:ob'sO 5ޤW ‚>}R}&[&o wª-VE."xӣ` [__˫?o_z^ /^ 2!C3&%LE̺&ێ^sCD.r R2sZ%ݣ7ݖ*-)K"_ _Wg O\m_F>15,›N]&~ؗ01^At8 8#IcE3-UXm{8+툆lDXqd OEB7}ጇũHQzWe3]EjE,.2A;\-PѻJXS"S՟5G8ɻ{  ҥ ekD+GpĽ&is#1:2ۊs[/Ûwo?‘K_dX#eOjWh\h_x)A$%#z_T`f-*rx$rͳH*¯"ye^3Bbgoޔ&< HEyH̰ʏWڄ1`_ؚV0~䱑"fݩGr ҸNUa-n88cs5z7~\_ww!>4rZlKg+@?~ƶ#/#TCwwm7]u8#ppwk ;DdmkkL/ uiIdeoKǏWga[sRz}RPX]t+]"pB_G ӥk2і=z{ /nk !<*_,1VhQb+j>]o)iS Q * | =}kqPY\V[ښ{i|#Cc1W}>L?3O{ wcԔD15ZOqNW3zH5xx-%)cG~.1#9xV9~ͿO灞g'J!VI7j9#08ɛ{DtI6=DHmIѴu2nmnnH}RjѐW gڌ3-Q‚Te`w|WBlNJXUl˨jS)8vQD۪4lٞtq/H!KtB3bI=zlx׮:Z {p$҅ʡDBT#du Ӵc%yM,% $4ȓqg_WY|=IG.1ڎŸePd-&|OjT0Oߖ9ݗDO԰y?xč~8@IxǣdӄčzA+4]RIKגeYW,N'7߅ΞRrH 05&ak?ԏ,|Gw+h _\#ݓ}b 3"b!P)T6YH帞 &`|0CZD` a,VSraa+K}Rrgj~zDy1Ffv~hχCOlVAZӘ"j+0xdi>pua+۾R<>_OMs&Rr] JN`Bxq; d?8Gdjf!%/bFlɼ?攐Zꬬ5`"'{[9c{هq-~pۊ@n?~},"Ad ,9y("(|7*l23τ 8:8iBQ\0 39ыu1?^_]O9EXZ_( go¯{ߔ6OA%`O1!8yҶ.l>údᝓ >VECbw\ٺJ1ߊ|8"B8ݿ>6¤F㸓cPL>] DO539,?`?*q{K@ m,Lh=sQ\XE%Zʩ'URR *^G8ɻoNF4D&(J{vgIk- CBHb 'X ڔzE%CgODPj0 22R Rp zZoao/¿/ +Jl$"?,zGhiM$G}ˆ'x8_ RXQy|K**B$ƔI= _'+.^)Us[ACR;9GT2iU `e9}ۇuy@IDAT7ށεE*nȔN*_}N{UGpf'y3?M"I,gۜ|`<%M2ײ4Wʲa( *&ǒLq}98;dr_U$\ 1YDyo %=#ӟK_2hJ_W_~m(TPT;nm@t(%bm5D~Ҧn?-$7'K-?|L+s~ǣ.SS 3VS?F08y'{8NƧ/ܠԍ[19DRYUDw mKIN+ʞԁöҦ:C#}KFsHD‹8=B08T5Jx(L&g&]қ@\y&,5Ax5gR5g,Y5L˩I&b},UFVSfW|%Hˑ|!S ^Sd!o@,mԶ$*(#<ۡymi1qoIs"U46qmAה5J>u|yfQ6§+=X;t2Uhu}ZX_MuX _Ӿ 9EuG $SCdH)Uo}V J{, WuQi'(9Ku1d/'x>N%s]9&*%JEѩO= =?hYDgq*gP$zUMf_4)}lp> e[coRn1 @BS~ %\r||=Gaiz(N -?3-H(H01^!߉ NՁ1Fu n@- L lI:DP3X=oɄMiA y;/5i# N$cUj|l)=ORc<1'-_GpN뺙2vHaZ ý\yDxULyaٱׯ^2lD^LJL y`3-LXm%5Ox][ $/M=\/aXWv dk RXdn: 'ұ#&0,HT_ҽ0+iBocČ0I W@NpLn>cI[BB89GJ `"s(T4#N4Aާrm5IcwyF>4wʱ<DԨD6E-/^⩕|@Î#83g P4I)ؖy s?o¼˰XO~ffݝm~(CrAr\@- :%=f|D¢.iG?;уE%FY[{uG`p7oz`Ckr% q)elIbXٛׯ?h?(c;I}y0@G_*1EjQ |UL"K&FCt?E[;dfZZcu_QjʑC_oz`XdU4n?qr%bc@FU/wA.~Wlꮤik/iSѣ+}&Rg;#a&<.ȯ5 6$ԾcL@Hh,I˩XkpDIlG)NWL[]uUH08?g"C)[`'2yFIk[1k#-/-e2Poھ֔1~Ycpy?, !w܋ѓ9Fi'#UD~踺J%*.D%~)}.5v bV!aoc3Jr:ᄉ6%Q9o&a6z$Fue8Y=c$1mdE8P?i苟rG`p7o c)Z%%٫l+m "3Ҋ{)3nGf|, & ]3_lm~`91nH~pY["ݽcf[6ғ( !aڃ4u{,_ַDƬlA~M~teD"ǁ/;p('R e w2MϮCNL\zܩ]dZ)59Lj`JGkh0ذ,\߂X:"s`n43HH(tFP& (/A8#ppw^xM'VAzfZ |^)V[O;9)sڃU)Z[D*k½ibK/DzÖ5 c$=Fl8o4N7Ijߖҧ\NYNd# O&`vd-]R>e10T @?`Ɍ@R'1OuQFsEu [VNIȞ{H:6U-/#UmY֌mI@Vc-\8f̹  /BMt΋#8 'ymjӝT &gJW+:N~2? 塃ؚQ[Y^ [[RZJ@l(a1f6&Gy&kLN3 $1&Ll󷪍ʼ C}*oXj`"P$J[0+[%SdMi_ uֳVoiB;Tt2v5&8|[.+v=C ;I};9M+2VV2a(yD+swQd(ɯFABb &g*y^ϻ;#ppw^gd2Vj"˿ =Ehrgs?x%CyG[k :*lPC$*T4*&}H|8 ߾Ƞ^SRE1NuGAK lh 4ad:ir{xKKa_~y=UmQu ؔ‡iW@rU%Iwe"_2W!bIjP/r3Eȶð/Ħ\/ ʊAHZ"&r#0ee u tM#mC"fWA\lAӳq9;mOR$rQ$ e$J97\=|</|E`f[/6K wki~ӼWۏZeSIYQyPu;JA7F!"Ԩ a`3VOO= 'b߯Pի8#pk`^r`Y3+Xެ/դRYǮiOPH$y] Q]@)t:F4UNN>C}DaJsxJ۬T C;*tN)oEj[&T .%*Em(!2d a9/H DK`H%4 (HܤDoEg꫰"<ӏ=Eێpgˠ(NH2y;H.WG8ɻo3 @GO$/H=ѓ=)S4di=|HA_IUEI_ޡLvccH"uK$z aHԻDS[9bX%B>RC@x'o('axVoł"meW Z"Я|?˿Եp릺w:qO#fW1^um\rL)DݨD7ҿ/wbm /Uo}r}#8_'y_ |kF |hKqH[Tt-yXi"ꚑ-r֡#P jL uIBIf<.g8UVX[dTA+_΋#QT=Q3Qduo_&Z-ɒ5Y{RUW^;M-.,Hk2Ho!Wt5_Uz(%q_% È/}~^G8ɻ/!&"zRORMzR$Ih+6Kb{8i=(*H.gJ޶r-6Yf#zz.F$H<Ԛ3LI]wuQ9':fےRm}!aslLYWd1~e5&AT03=KBz 3j܉ӻA>d` yoľa:lA-"|[F7m[I(u9MX#hݬ|H8#ppwecd&Hfi#i?ȧ:C.(Y+ U&.H=lɳB|69уyʯw֝E/k#vkaimiY7Hn0e X z_Vl`AR8&K[_Ē0DջH3-,1=۾Xbs]xʂ̶ f0ੇ1(tt 2'T2SFuRs"z>/#:Nf _q|#VŚJJ>۱KZ=`YU<} ,OmA"P <2EpOT`2OhǻFL-ӤiJ*2. By򣖑ߖSPF߃cL~gA!&`%H4~@n8o+{UKqO1ҨoϞE}_,uve% @y)cݴ9NF[ؗ@?;4#8x\a,j-H%!؂&MPX_NS-BE%]1Oe}i2-=Qk5*!?IIimL0M_~wjq9`L+U`ѝ.dt[Q:GҮ.nXjTLQҧ񊤝;NRȘ!@34WUGtm/Yup*C3uIۼZdda`麟vG!$ATemkR"j2+^`}fN.(_ˆ'Q-#ѣM$/!"G-J8GW/{Yڬ+B?2KNĺU\K̎xqG`p7koc$X ֓ k4WWWDNXtYI Mȋ0CnXʋR}^)GXՀ95ɋH'\p&+aoB~ZTDTIMSզO0*EsÐ+Q-gDXS(ӆrT\[)U T_ӝq4ҏ]LE="ͷP'LnSH4+b,-@=>;ä)֔POS`D}#8_}3?9CMv=Q|":A޶w9*VÀT5UާUtnD1!A.{פXp)V7;ѻL>^%|րqG"[bl䗥1+B22Tt,1SOjTD:dϞ=DՃ*)r̹,VՍM5q=$+6 )ȈUJS QOQEX raq54"l 2f;<~0!-8PCC^)SP41߼n:TqN!̒zͰmW䭫oSP2@ V615\8]@I]xK6i09DRio^;T2WD4$=9W*e#42"^Cjѷy!7Zh4wjVbdN?*u Y<"tuSH_>#T l̯eW I|!B"pN@8DRu=Pmٝ-D>!xepxwES7=D.|&IGIG.GZW< L#TUo"lp.I.*99G.Eۈ}0:#p}ɋ#pLY8 Ek}Q%`,j,:^GO< ;fd ͭxlh@ ѻ"/GP?,(Ø&X9l^b?| ۝okr_80†dD֕;򻕆E `ŋCߴyB-cKjS]:̹!Hq /}Hz-?D1$2#ϾtpVu; 鼈2j%;F80/6%BAtM++cuKp'ϋ#8wWd}OL[80ܑw,ɏ̜h_ze9H}#sD%Gy#c&! !Lْ=p3?;e$Em+!{Q5U5J8\-_F&ؼ2ӭ[߼dU)7ㇰD"G!wd(oK %{o 7) jqmrӧ-gpgUɞ5?f*~BnEWUk~т/"nrXiԻԻ`I~z%GS'ͬǾWuG6!Jmz/RGR:"jeiޓiRH*drLOݟJ Z2M*JG>T0ֻ%way,Ar3z`y10,o'MRKYv\&[Z0_T2זi :fkI&T_H[. CCh {") Ǥjȑ=oI+k uJI܀*%5;7Yh-)lWCSk,ʣ'3Jx~J]*2 ӻoK|O{ODiQ8ѻ7m9Dω{HJ~JEe0qi̋,8e^L #kjlkB K̓#K7mLq9Oek6WZvS$R}!I2J\mE洮ʺ9I<΁uz&ڮAH$0ED̞v?ZA(_U,9q8d4!r,ƺG/ˉّks<rG;wʇv}v OD_jzꃰ*¬.%ufJA x4p;;>Kerd*hLsi^!5$H0ٓxW$U X8,sZEi ĀT$dzkZ!Tb&K\{@Vȗ~\#F_FOcfSwR&gN>#vjgO'?LQ`ҬjbGcqXyIDRv7NI(_EQ}EnJ// Z -/ 5@?ʕ @XHBV/T/(Q=9YAy(, {!I:`r񻐪O>n 廈Տ8wi*VξGTrM@|aem"u])C[ a^K5}eb&vԿ/GҰTpۈVnK3:&[OddhTWS#E{a^ۗ O?x=ȀߛlgUXlRX%έ$Ѻ|CI꽐ǑY `M۸X$LE,}=a K(—/;#8x g8,5~O$e܂\I tssT+|z >0KD/(s_(2bI7 5oc,dm^m7|Y)TKr˄*/MkBaD*vJ*LX yK UOVWC=/ 7ƈMҏMhI7>edx)ڬ|,$Vh#Ugz7B J7R }{;i}̴K (Ea6` ͊,G|EՉmy=XGpN"]egRt_*=$=5D@A^.* ?')zT":tAf:ir^#(%mv( ɣ 8<ѐ/XG}UoeԆ!GY%)CdG#% )PZL0Yc&%VP]Cn.)ztB-RuL'aL玻 9㳼ɤLߟs\+1=K? uf݄%o9ǩfܥmL0 7*EGI7þ?Hڍ"jNcRPPt>Tp5"uRY>??B2hgvO:#p pw _ʭR1m/t@:YɊA@H/T1aVdR!u*&#)J֤mTB=z4߉ϝ,h"uCFw \8-il+Z|j`l웙vckK>D1 poZ pLG2{b\#0b_$DZE—vu.;~Gy sπ x\3%/bMڹ/a ʱ:ʬie|:&vO>6YL`TKE B FTWGod{򫎀#pX~}} 4ab$뀊Hj9}G#m6((,֪/~vW?{oWmcr3A31dTU]!/m&kEz ]t6U[KR3#3+$13]߷ЉI0p6~ak_Se!F8gٙ{12at~PJ?>5/x;rC5m umrZH{V}8b NMOe`j0z̑, (u2'ӭv%zdH^&˫ |dBW1XGvQax`F3f\ :Kw%M>7mۊI E^0.%>mJVVJґo=mP L:_Xt?T ñ@hX|=0.EOsS8_IFAtY|PC sk]aޙI+6`d(s(âez*wZն ]fs V*Z5&Fg]LW[J7Q9nw0,ڱ5{z d e2/FXv~.<ז#H3ΜD ,QgM92մ(>} Դkl5q^g-~3Q'9u($AI`>uMWeUGU1vG>I'%{"srT՟~hdEl|=-^V뫝wDX~GCCGsJ&y*m?N]s.WR{:5OY*]454pPw+03z=h(=&decmjR޲pUPc}Gs H2,Kx .6PyA5R m`/ Fz=Lޖid,ӱjoYJ7kZVGs"6z' 2Z갅, ;= *!K(Gx˞ $;_l ($$P P6 hw'P`ieH.\6Mu*^=o͌!Yi'#(xfaL&bsʸ}C/Tѵ yN?=evkX.$hEZ3x)ziQN}{CƪE Lͼ^TM9(H=sƾ䌧U7{0Εqt@E)$PHJy*z}a@X$[dEuc/}NϞE"ӻ_fQؙmnoŅtj*ۭʙ6=m]C]K;!p<.,Tu$IՅ@`%{F`M}yfjfn-7|CvN]wO˰n#gأ>sy\VP@&hYdeq:*p$m.WiuD֯7N#=AUpZkƨE#7*KT~=#Ν/v /dh]S0E8J3Z'r$^ ܷXjo u`aUhj[Aary/XymA ($&$P7! :P_G Ԁg: _O,I=;{:gڮg]Ԑ],{09n-2+UXY_wPmv"li/@`qQg_x1@:l+TҲl=^ @955Vj}5@Oֱyy ,)exx5"h; +NzZ_q4)V`Jeddf:NاR Kb/C ʼ Y,?uN&[gAPZU%71j{]0+sw@A3-= *lsæބG#?d%B9+)J!B t.X]digэTGm;7w]:iK3?tjTu&jaC-_q.[ss}E`!: @4C@e{gz 'uP>M@\JQildv{eT݆,vu ^# ;^:vp<6:\x_Oy GГ Uv>Al)# Q{yL[ypMd]w̭^~ <&Vxys1y2@jq(S 1L$2`g5`1OS kx/$PH%"ʏ%w]Y?n1љExR y̬ -pyzb:7w&^T*7\ym+>S ɳlX`Q%FP'` r?u*یA<9dL^֧gN.,5 wL6yFM[Pj!mbޔ,IA޽3jmU*a[Ap*BC5^L ؐIoCAcX>1b_p$%y. ?ջ+YP:yϳK|P1 J(Y2Y8M޾},936~\8gC0"?v`DV|N`?QSMCV&N^ l L`Hâ,7A} W hȻ`?ʿvj \/-Ee!^k./3iNd>{d ~^8[en(u7z8-:6l=Pֽ|8?bI !/bd[/Ș=q9N8'@!B_2 kmTV'R5kI#&6g#$ ǁ .:@6DtxZ$,zQ\ȤQ訚|w{k1FP3LzUmEKx:U39C&Y*K8h'7;3H0Xl1vb"Vf֓4VG8g@-TPp&h 5"hq8!.<#Y3U cL]#s!3gEԦ8~~ wi@Gc>?m=i^B|4A~6_@Lp0x7U_GVM@!*k , x(@ޛ^VVU6s|yUY4+e ]p PaL3Uzs?i?@ iv\r|cڧ[s[5YWrV3^<\X .IL#-&' PvUj[v{x:U`h2&QW ~ |,@a*Y6Nfz|,ͨZPEQp,eT^'R~ )j`ǿkq F*oC@OT(k %}NWμȷnD~+B(##ؽ{𜫀HX={|pRH@!I^4,zhcmYwp<&͙>Hc~a=AkCuRb}X[L}@IDAT:??[<йHgx]v?='h53vmyA`ZM%ѶMTm5E/W20;4aA,KgZU8w'2y*Kޔ*'{6P b ";uh}ߺnXk>yl/HwS;qg  H3KmQ 3:𒷐=/8]H@!W@^Yd ̥,ls SL \ fO|Sؕ"UY.t et~ &̖k.մ}B "jRul):Lzj.3Yl#/ +V%h[4i?g7mpi0L[*] HH&"ITT! jՓTa!ͺa?^wp:U47e;X,Fn1QwV VjabOcq 5= ? `З[A=0[\3 2OT'D($PH(@eU\y\ -!t6i7$xs~H3i& G,zz6hIWa {pqm:-cu{cmNQެ4 q<@b oO?iyZL) en𺕍D͊$v<,r&ʶt4KFEM*N6%%2 TbB\\-@`ڏgL f;=>;~psk2mqa;z3D#Gޜs_Z zϢr&q (Rm8ZYm,C`Xd$(HF_9njqƼF;ܓk_~39= C$dB1EC0g z0kI !H\7`_xy-4PB`5cTfj5ú 2p;R[zJs̝_Chj݄ʝyEY8/:K4De#[͠b]grTvIeP2ɖ1sܨ: ۄ$J_xVUZE]sTϢ/F?jNh~v/V/$PH@Q*31LZe\,&LW w j/>U2wzEEXigj)Ҟj9ڄB+-pcv (6ލg>Ȍ< T= @r*72 e2V0 md.W*&m)Asn( +o4{ j4=؝ zl4JuAߖc%C+ vr&@? ƌ+k'((hP{ N82~>#`zSwxeϗL4=1Xa%u@4E ndlLH-̼mows3E)$PH@ޠצ<9Po`G}l67b\BoP"z#W0j9@̙3BE10D&qF{nF&ѓrv^50?.2,Ox,; ]\uYJ_9!2.ݓTQr3M/P6P;E,%݀>X,~Γɓ n}t!TG `0<&ƣ-G-S9Ï9Tew0PuH@P-#))@ID&0:9:iMxndxմhe<4-`9i= ,t^=|[@957p }WU2mZdeZ[ͳ◕]U/$PH" b*͖vwPm0.zev&r[e^D<͙ga #Mϲ9oqֶͰ%f,0 p+@Q>ەL.AeZ"L w[  W`'8ُN3 sgOJ tkp1R`7}>qm0OTξ:='@!B/@^,I% , ٞ Dl(hDLFN/ Y61ռ|vk]׮]K_}u\ ]=|\ vXc<].9d) ˳%dmd ρC޶x~]~s+k5wzFم<`>lGY t{8dx]f5,&JC,lƎ@նA "量}P5N[i8S}9TA[YAl3[I+" #g] 'b|*e,@ջ '4m0qc͍uXg?,Lo6M{i'}:[UMuR:>l!B Wm |bⷃw(/ 6lcX,hQiTagތP+݀ArO["b+j @b~HOAl]|dT~Yѳr(0h & 5xĹƞ]|s:%~~+^,zT^D 4`,SE$z gقv7Bwd*miFVp>|tQU7Xc^^cSE8>dm4dm/a(YGU =Y<!4#^@NvxJ)'cidL7*Z| cqLλgPl-3jsL^q{'* gbgT[H@!"1AOE00U}FkR,[<*uJ2n=% 0[̫E岨ո>>M89&EmK/v]Yh|\ۣsB"=@gQ iv:T0T^+` bh葊6=A8!Ύv{Vۼؼ q@O5p`,hVNvUેUmd*8XĸAy"8CVy{!uiD4;L& P "iAdS|i<ǭegꗿ>ЗWQ j4Ϗ VOƽ'*xv[**.*$PH$Pɩh/\.kgϒv# /=Nl7nӰ0l,㌭ <9X.Ís}E޻{6o)MNs9iDŽ`k8z`Ƣ-d~f}ūui&p,r \)~{ݓ_x"oӑ@gn۾ P˖e"ex0 3ҝDE!.d={5==Ro 9M2):T " kdo q% 81GG޳7{#eG79} R ٵ-*j~xVO lxVg;}G' |(@qIgjh̠2t8  73X/0i{h-ԁc8gx^/ 42C[>Ru`$Kd`]U+;7 +hp[<=̯ݽ{Gb=դX"΃3"&(ob[/x]xv`jƘm[blWVeN']Rtg^[i |i;U,KW[82̼Ts;U-Aq4Y[Xp`hI,T 2̯*>ˁU @hrg!hLy^ ҢE)$PH"-36C#[W[Ĉ-+7d,cga»E8*Tխ]ރ훤, ,nRBz09Pڌ[A,.,; vi;Ԇ}e@a]l(Npqi9ԞbNXMV%TL%&c#pn?r7 p*Gc m"t&ܭ~̚L\ϵƎ7% o kC,.`G Gq֘4 aR6O&3U~Uann.s˯8MMS'&mjgx~řB 1  l5$̧@b= ;Wl__%X<հzLO0z"ǦIA%ӣq2be{2{-e$4{{<﵄f~avd;9εW^MpQ/Ɇqрf)bFO50WZŸu5cLee2zU0@,Sgqq6Nvv`"l lC=4zdTeJXrL-ߕq>YŹs),TE*@Ī8kHe7SO9ŠvpTCmsƹH]Ʊ\v{hfG%UluɜMtȘ<|'d#A}i>גK_۾=TE+d'cnvv/Qx,?NR|2azf< HR/ uK +>-J!B~  ǐئ u@l':['  k՞, vxzY9c w D1{Ofs!HX߈l<ٞQ[9*d1\YiaXگMɀ1Dl`?Oyf0̈8OG޴Z !+(q0)| ̭|ktՑL/ZY;u<u.YybsCԫ|:9l7H uCSӨiOթ%uJ$aP*ge_YER [1EnNIQ\SH@!JPמTRu_,|*ػa&@FƫV-m +pNOM؄y:'sX0FlQ, πYBdTA3vqękliY*^c}+`*8L-Bl:u<2I+yjldZXsvb2UDc8eD, c_np`H3F=wQk=K:Rb.W=Kx}*SGf b3~ O6I-@"uQYZ* z? ZN/W;s䬝^g6 i\ULdYYX204s:$ؙu (D2UuB!c` cU2p~MiwUz|X\O=!]8 #V6޳SwxcWͬt?JnV8? M~4dnaȫLx.-1u9[Lj N~O C >}vR,w2@v7o`w+TTVϧ޽jy.HWL{d at+#C=}`I&J83V+Ӥ(dAӳd !/̢zZv2 *e l;_`e ]#8C靬+G^Z&X@ eleSC0p<$ >[?{EjFk`c%q2-fBUrnjT1gp4nPHS`+Ыlk#~(@qrb6'3mѾsItE)$HyT%D "bbY'`B,K %p0PNfc6$%ʀVFqdWp 64㵙Zmcc#*]bVE f?dKf[|\_&ԝh{jTYZXYϲwrqOAxaQ:U2#8Z "r3?fO1oSaF: VF[iDf5z4RJ='[5`0=l-9o m Qx X=apP>n77xv z߰Uo;5˶?jx7ZPU%D= ^Vgmʸ,a>E)$PHǗ@~9z $@Z<PKjod)&&mfCIUΗYQb|=Y%A^֎`z"tQh8Yݫ6̢1??@\۱m aN͓f,}׹W[ ϡS=@,mrLڶ~vtRytZۖ aK8F*OtIԽϟ}ծ4,⸷ άwv:9-|`=?8P{a+Ͽ<ogyűn~qړtve>Uh/jHs;M6S5Soi)d^V <_#(X#7';Rpv*Mk@а]Mut_[9\w6Rau/k~- cW;# ]7ǦdNs\s vPg #8( Q  hSc4ڇhnrJ,2*V2jz;">؊UKu(: h+sy|T=Րg"vyfPyT9,9%xG?'?qV{n޼LXEO֛w參2"Bʘ]XWc1s 0@t=p^qzҥ [Fj6:Od{iBeϛFx zBlFqF$c?Foe9{x/mmvtk)]MT)JWQw`X.V!y\Ę| Wx`_b=6 F%@M5Wwp2}l4!qG~Dc]/ 9e@ ^n~fcT 2oow3u>\ilk-F*ख़e eOZq@^Z^U.mbQ9TauлE4hft2JL5+;X;E'+ |m̲Ǯ w ʊAtLot 5e_G?OTqҥtŴ<] P" sg3G6Y#N@%-/j!!ɲs=[m;@{W^aqFurLNb(ǘAVijȳos}3TdzE׃m}:a~*8 }UBtIͶ_JSsϷ,bp p:Y?ir&юj_l/EI-gxғȽ0kSVQ[3) @4#¦c Z-T7Mpv.؆1Lgr.dU3Gc'9q.~-=MdV_NlɾFڨxN0d%蚰nd6<p em bC捸7%>d GñmIv}:neBTl7k!vQWq8Mao'޴хf*aGۻA 5ôtrOsw8V!`x}x}5x\/b}9\ı:!XS'd\\u>pF+<XOU,^_짟l~}v.:%r .ykլ 8!|1T+ۗ){7(Hӿ}5iFM6);A:}W#i7!WK0vC;(eISa?-B̙J >UF{CȤ-NXyN/a\!G`=M*uK'˞@;> h~\}~<̇A'&~ du8? cS7Hwq!J>r-17c u-!^[Aۃxxk),c4 QѴO Oc@!K ? Ņ^]뎿{/OH%^ ūPfo #/F\&)7F d,mђ N&`x^`gdN.jJ7=o}Q,>Kd|ȁ^7y<92_O`ӖNujXoZ|wn]XLN+ y$2+XiCFrJ-T07}^2@c /.9Swy{8@(f=./-{R(@H9Ń' K]7x;;*# GOh˼]L LO 1 8`SxoU2,^>TIKkߵ8nvyÄ[_RLujOZ0Vh@{ !-]:f-iȜQ%(v[H^/ў퓏?̥:2+I_aV6dm)3,ixhcU"X5(!BYafLEV&jH? Oc`Hμ}u_ ve26D2aTgjQ%E06xEG2`ǞhǍKyBFGAT`AAC*~^8'ƻ. |&n뺑n#^=#.&&;8jX=s|Z$1 Sie1Leh h^3 0)5=;^~ms`Ec_|%-7in`]10wN/k_s|BfofOl?Q )v`ӃJ)z?48=  r 5L|>&,Lx](@dQ1g1,j|́[fs(~-4 Rdj~i|o |p)}{zG8"6`d C]p#?{Q.C/HOvt3-V雠>a^}!r Nn yNbq` Qe!4)R#DcxK>//i崎G!.ؼFߓs~dCϰ.MUK# V>%$ ҟOk:'U'XⅫCm2q{fvmr"O_UuO\Nx~I}9 .{W?Tk;mqq|yRþl*0k |vHLWS&}>zx*dnD`UQk=f`p{΍ .mb{KlFx: M=у]poဗ^֔r6qx <ߌDf|)dž%LUTưBdXIm!?d:A-9s]^3]EQ[bz]KT0G447BL>~X#ے3Ar SI'i,Y;`㻘*f CSjql\GgE)$JyĿui E{`ܲx<"p&3|ύx{$}@Pq=FXPe_wOڏ6V.]s@9>.6׿?{ \,0zڎ`֣z48M_pC j_1'9{e˄DҊ:B0{O$b&2g\@`zHz,PE@k,ƻ!\^YJO`oߥ:*[j1.B )>mLA0TL.ť^?gI5If U4KZzBEayNC߲7Vɝ[GWG '|\Qx%PߍM˹ 81+l V Hyl\q]7_fj[ :::|qtl]x %_uЦb=|;]_Oխ5:> ;jC:AUC봢sf:)p`hj;)3մ;;[wnHMT};`7 nt|']&sڞ7HE'.)+K?kQ=sn o4TJ+ycO.<=JFE۬2v&M?wkͥ[! L'Wr_yjQ  %@,^es`Jz!AMT@MƬ''~_Xg? FGլ!.^, kܷyy{q|{_VcyK`+G*k?zMrgwZ4@IDAT2tFoue!m-[7w料F)-f l39TyOQŌ}Y*{%P}>Jvvjl8 /z{3 ZW+czۆ,V`o3??!RtC +.Og_cMdJOӰZ[['1eY&̙m@0_b*ɼN'汧 ;A]EV^ G;z_`o옦 ޳7`X{<ձ{Lhč7[im[9eL:X"K[潺:x ֖Y?L?fajP79B<;}69p.TrBO '3{^.B8| o]Sx=T{O0]F*@ Qw7^(卯=JOΦ'B^=2; z&G@~4 wJ5iA*+> ~iwUPeVdD60eje`p)._!SV ԝ9}:݃ juiVc} [8g]K0%" ($|uIgqa1@@׋ƖP2Lrs5uoswR8Xh{7mY9_*̿ϯ3_`oI=3{q%c^|MkhLM8!1@nԹ+_Ofz2}*Hv6|̻/`қjvu?kӱ;K ڧ!X v/ܳV?~6Kiw;} ̜/d-'x5'/cP^vUK> tqN̅'S,'k5a}_ A RPMޛ]l;]v<#jnYjk^S΁8-&MNk@[_`,eؙ}H\.Of_3Lx!cg3C D%ŹΝ;qlPFtҥO^}nڕ_Fh8}/TiUB- z$YlSǛɴKl=,dTmqчBϗ@/@0*M^0Bյk_W~ Lp읋wĂ} ^$s`x`1OgJ9kb{ı.1}MTeT:!,'9tcl[mGP G6~~!yR@>~J!v/O`mTMj7Uf*a+iRe'دlv% paiy}s8Vv 2N @Up驫ޙ-]UE`'L k-ߤ7rLbَu|H1+3UH)!,b<^" N2x=x.7g[P ~ZDy6x >㈇9/|.oFFu®&jRZAr/J8~叺>~-G7t<&{}lfq-m@%Pd@'mS|icLb4d ]V/*RZ|\.eXcΎy\> Bm7@L[*^T~^/h<>ZCs"(zs(UoNo| R}-bᄂZ Y ~_[Y}Aoܼl6e5djm3\r%}'O>,)3ВY߮Tz)O0j)Js.sҁڏmSKyyU}fښ8ntr gF5 fV[Q(}?Lއ;oav87*/z/YS lP[}Y"`K9SqE-mLE?=Eol`wiq)0yv‰Dӳ>Xt\: `u:ܪ'G`'9ڇtdp.ːMYSC韦LN3 {ˢT.}NtS" { a,&0coyg*.yH/ TƕS=~=ۘzNAydiͽ1DfB+I~wNAv(:  ﭛCU/Pַ6ҭ`<2:_}ɴgoZhy ׫vU2f}$f-sol7nD|wꓵD Gt 1 (0y'>$V߹s-X=Uzv): ڵ3FlU€I~HPjA_~^5;K<#9C9 e> g>yqө5vORcL* J o?nJ =F}'#62tzjqًZ>_L=+<Ȗ0L'T'J5D"޻5Eo?  wmM͹iޝmUW"Q*"J/TA1Ƕ64-~Z rqL0[׻0?}$KN}s-ݼu#9*a=B%G XW^E K @ 1yhC6oll"}OS?ad*B4읻w}1vw*/4np*\^N}ƭT =bluh./x2Ư@eOs|G2;{M\,s:g}YXt@"c3o} -+pЋy V)sڵm…鳟~r5kv|eEӚ  {NJy*c1^쩂MN,uJ3;3-DCrG0}Sb|Tz6pyn%Q[fI; m:ҙGwG6xZL[ÓLn߫#("  -+O%Є{, GNKcʹTAŗXC{!` #Q?>𞎺< ul|,dx)LtEўmjz* @tT1l̍ '_AU:{yke 癹3G>I`mUܺ}4i 29ôD>zI:޴RIerۀ)@P۷3/+qlFˁvd]SIuc*PǞujߡyj;ԋ8^[Ke L֩?JJ7ϔ癩y-ͼJduGC:jRoUI{[0ڶ?(ej)^vC  =eHM07NIO~DZݻw#'G6 +Gvyz:d,xOla3=_}8>z[Lum}-<0l%bv}(N9OvfVNάsg{22, B>9xֱE?/KW z`b nYˈww Nr%. @e=$wWP+2f)=sJ: nfMH*Uj;wo7ua7KG .?Ht=} \ i&af3t^(KRz#$<2r`o_u/Εy=:\%?:9?jPޱ$sގf3M#W\1ww{ZkY.S}4s~ͼ&s?{w݉vU~ Ys"t~Q=b4:KU"Ȗ ҟJ'|VVf.`z dXʙE:i8>VzzmO=R-zol tFXyJ`G, hd `B^}]uy %ijgc& (yLݖn[%m+htHaF Je޹F35A{!_:U#yb|BIK/]$HCG`_ϗyn{ug ? w(=I?6R ZC6QDmdF앍eYM#A$_JaʙK5hXA6{q?` -y[E&Stz p|N5p?!, A6qeE˰!)4`kSv|H:Ԩy`*I:k4+ j٬N#c? L" 1f;wnCE{W- _6S='T*lfq]4HwCxQP{%2.J,٪!oW sЮ_o>/|t% c iV$Fd&lͥ% *&G sq J0z;井ݾ$sF?:|DxzxEJTbL Ռܞ &b2"4(l| qm;=" ;"+$gJ8WC22= BӲ8a0,V9Xgzt4Kqt=s`\ӅӁ(Ijy?b1LSM!ÁhnͫK:ҰW%*ۺ?? c66{*4cQ_+yFevجwO] 'RnyĦ~oK@zǼ|XPZkHuVV f\%wGر3>3lu<խRSKA"RQUOKU` J O׌f_O8`@^Oj%VOj)(I nC_ߔ8T֦ j>ȃZBbAn(Mr$ʄn=w<@xן{zsyelbL`3I>{mwałavTgFC{TgĚ#4 BvxPbvx^ L#7=>b|nّQ"WKy@.g1JYY< G K'/Qf9EEf5$q*p5RiIbɐ-Y4-$##lT6M,hP]ýcosvqw}6*΄6t ! x95f4q[bs,/\?H_:rU(srjZf0 ;:&tc{כr@hp(2'N<$&8zp&uWdNv5@ T>5]?cؖpĨ@7dslV7!nUpPU궑f0 50r#&@ԶaĮ` eynqQtP7sަ>VeKfSֳy)A`LM>†ԑX TM5RE-wQ: k.mH[zN3'WK.Je W޳kCFl̬+ 8Qצ^ߏf̰$C~eQygξ"anWvQfoG&G1~^BbfSL~ql|TSVC*9, ~\Rf|T)yL,6 ^m0f  ’@Bc27/7H*&=^daa" :^AբӺH/sax.|o^Rvׯ7y .;HIގ6L[v-tT E3wp\vM!fXv"*D=>%!Ghς'=kOQIҼbvB}qܖE"YѵЋ!#UC1)1cghw_e"wnv6}iXb-1W b!Ԇ.\S@q*[9RO3j;B,hv:aw}}N#۪.PsnO_kJ5KPm»yXy{歛]XBPZ\O]..^au OA!f]k>+g+>=`ѬEyP]+xJR7swɖFYA=vL$$wg xž#Fzق}*$);=< h. +csLOb@79O|~ N$ XU!P mMpB-y\]z$H KQvdyHhBijwq woj4~F[gw vkFw&ȃJLF:Xܾ}5d G-߅ `,1x2K/, K:$ ֿF |:xc}n. 5ZzS,S{-kǮz)c i|_bUhhnQB-1 )ki0K`c!6A+t_j1^Y:9e`9Q魖 oهtg<@I'DYH.w=؋Ew'%yJѳ@ ܼ>]4Ds&Uީb"( NHxUjZm-ɓ WsTsҸ c8ܻwOѾVaQQK)Y8^\*gRceP>v$L;2XHB0+>^W`,*\PfKK)yi/8lEYX+"&$BwXAZRHH|qIT ]TCf AA @ K1갬QFT@ ~y崐M[ K*˙3g܀|UA}G*衪y4^ۼ ')M^:G˺ޮ vbɥbCn.ueGikl(i번d2:毛}բUeqS;Bʭ_["wLJa2G0Xyjj{|xգ!(ռdޕ="H V`1aBytu%#]`k$y| y؏}mHDբڰ7ZA{_cR JMxn~G=ҭ~nU%c 2HFV噵vx|#Fr59n̟#r#2МJ' eI9$ӈgA2FhA gV1AZDH?ң?e*`1NQD l ^3^Mg-24e_ "Ps޽{GFGǔTIUo'RJ6JV&} kYPzab @*k{$x0rq>ExΞ?1Xyh>JqI`>4-rbPJ(u/@zCǼusje[b dK\5_1;8R5Y ίUZ=Hc `XRG@fh}T97]3< b^&Lw=$h \hAG;6@ML&2GsKؾw@F)'_[vKj^JŒJhF@EH;g@-=:6*cP!k% nȇÊ2Q:01+(d. pܳ~Ppev{yr荌&s;X4ۺ-.UAz0+:%mMw;ڕF15> MKvB-K+hVׅj\L.i[xy ɽ$McUcQ8}FgrH:99UMDM`qY p jC*2f͆ĊAYe>z rdޠni=ns $[ْ,t2Uc|@ML1gN[3#Dhg[lW}PS>/Xd *G *-B mQ995)wޕq%j2 LTv8+, |8 Ua:"u~X9+!O % kMyڣ-%HLC8Xn{~xlr4೗BPoR8qKVl*23W< pnD(kr^ªdצlyw:QXzESA*ƱYxOoJ A3M'uS :yp͜)HaV|jlR'!Zh[P&gja6EC[OcJ 1 n}c:#S[ B:;7!Z_ m݂N?XXO[Y1jT:;LA}-SSSoˆף1Ql  o_!onn^-֍Fzz&m Z5n<œnX#ĺ{mJA+?*gdl#_C5ɶV;p~qޖ7JcɄ6)qIQ޵As>1xfb8 iorpMƪ!KjiC@M[]Z)錌BjVnÓt ApGb^dbrBI#90 n)JxԸ.@lH3ռLAư,DˏnW._,l? KRKNC*GظLNL*)&յMMNNq/(C.tq|h ]bPb'~ Z}"[7ŗ^͘zC$tvnbQO!kX_$/n{ȭkňV~cIgʕ4"&LIʒNL%!G ?ͧwi;Օ&r׭! r)J"t1]6z`ϗ\-_9;TNPSӳ uː2tn*ff-s$w!8ku7wvU H0lY@RRCTxpAJ4*GĦ]?z2 "l^7@OJ,_al`ܬJHY @U^4Áv9`@^3=k/!#?l C&"U xX C=˸uȎ*SP.)y9ۣ 1ĹSR-wΥ.T nX9LCjy}Rbko #\ UazCw=5|}Qjْjҏ].)::R%xH(c!exBHʅ 8`@>+Ԉ ^o-UH 41~rB*V*# P ?TҰW>E`7-CD !H\2A=o XHQAö0'!Q,@BT49oB`Gk2n!X*p ?Yʙvje1TB֛O=-/ RHF`sGCFGt TeK pQoZ:TRw-?X3w*#ĒW A2um4* kSq( `GG\ A 2rJ@ 0Ըpm MJaFhFhЍ0)#AÈ$zaF`ôecfڵ BX. ?Щyc<[76n q7ֱRU6D` J% -@)Ǚ,+sg!F߸XA7NЮ# ;u.1Kv3Pa?EA-68iCtj[e93:4 wvjdJIotfzp )n愃8`@A1O$ Ke9 sJ%)deŏ P!E@Kg:c]~{ԇbC qqа{I 6 uleW@IDAT, gԿnnuuEܹԷ6,Ѓ.(I^D ץ'0U͟gY?{V-{t !2ಒ":AuKb:+s]2mpaSw <#mx"qPU=t(;YU-l˲ cKyKx9*Y NjrN5 yxPbǑK ”Sۆ:5@Dn߻'!X*R ϒT0LUC*A`ZCdnݺb14 tJiG X ##E2.)i'(-C03C0u.QzHȏ e>ƺP,AZ-iI$.|npǶfitwy$`K?=m^۪4h#kM+ѫ;P /¦a򐖸xeeY Jv,2rGVeb!_UH\kէy֛7v­KU cj4RT5/B"Z6Fg歄vCT$H*ÁH,(Cy̸qAG+?;ܹ`+75mB<:C[q$=wV>UN\f`#CƟ_(p6{;zY4! {+&A^3NE7g^3hQ䠝PE>Y>:yQĮ;]QHG' JzV%ζ{U]7yq <}Oaw7]9Tݮ=~[˶ G^攥.A020P*H 6S1QJhwu71tXt#%jTj?4zi ¯$+a30AY*` OZST,9{`NM%s$ujV%CAiӑpsx UqO7Pj)$yrauF}kApVIJxHf*7ZmDF+H{6b rkb݅2,9&5S|sz%gibۚ%PVf״:mw-7~8-“ӆgx'vs-k r y[q j[+.ț|r߉Qm.o؍voz N Ammis.-rЎ 0A>#unR.sKc3icnͮkD{/<2C']]jvnbMْLWd HֶdK;İ),uWdxL.w=@\OԬmqvѸr 4=wcm Ż*nZt8]Al}aL\%VF֋! 9\H|vx}zn:F-P*2Ӿ )ޛFMjKW2|႓l:l9sRAY0 %{h9a'?sy䱭J*5|!NZ@f pso֏x@gl8J}֩V!n- l܃=^޶2q_9`bW3 ]4},HaFxXelG<]Ҵ !޽#s tQƇƣ`H[@ VRNa<,cyBReZErCဗ9?G'zj"!e /mq+ĺ{3jڣ0=lU\^2h9J2hGi0*0#ڼ>h0 Qop#9w6f;AG@d\&->?bYvmݏՍ Rd QM[u҈9%Vk]OlssPVce*XjY/ȡ)o:-3\QKu- i^ŗw'f<:T?}ZU'IMQY-?߻D쪧""2l,۸,xc|sI>Dj 列Zwjop@MuFsp|ƭbz% Ӎ6Z:SZ%,ͻ< I0h,:Y|PrS ë6,[[[ڥ kliX4turNj|IX<+ i z{E7`6[%D|륔Li  NRAyr?sAL108&($kv >Z3am\F8d4P;`0YjZ64GuJͱ^ܮhG"GtA_=уuk7 mwY(,) 4/%!}_ꋩSI^Ok7ρ.|%q 8+o\ sZe Pul>8@XiO͒ ƇMƩqvԍٓҤ]f`.r&bVWJA~,/۞܋>oԀ>A BjW$hTJlUDi5鈡Cw]Z;?(-Cin~S~C44A-7f*v&7Zfs$ '![BVM.~LS\J-z]s8`@x8 Caa= +5 sضZ|-|ϫ~|/o\i*eM\O㼏 |J[;Er:.)Yru&C58\HU7Gu`ݝ ?5\+c୏*Og2R 1Z-?J[w9]p;ęr޳h?']ZNu%%y-ihf/&y,<ǶǶR(y>^_Q٢14m j\*-[^_L6PPi0~tz uTEM=t' <om" -! `ץ3ܒ<~͎UNK\lDƶ$ GyGb9p30|,*7K*-Z_[ƾkO-!ӓ q<7qLLYZ,.j.sPrT0X̖Z&cմ$BY.-]Tʦ < :دtAr4}ሄ, m>k ?C%Ҡ^U'CG f`ʲ?LS9%p?~hɝMKe|2aTWlݔNϽ6='`f D႟U8O1P+ƭ9<]_/5~bCxVY=:TʲO~Eg|<6 PDN6zOOےǰGγ顪ua 3m^T2 3{>bZ0c  !N9V )h}1{H.fm,'-մ&eYyݛ Ҏ"cZ78;AS+%yKrʼnSI:2*>g2psMOu F(eN!pi0(B-IG`HHUH^yM0$NC# U-ն|^֔4caH@ޞޞfÛ"_`>s}P^^=W/!ڐX#Ngo\4h\Cp axcveԷ퀼x]8_^䞗ime/eBŞj0j_<}# -%ʙJr*8`@ީͦ.B1N^ Ҽ 8yTrn䱟Ei,;/ՒL!jVAnj, %U J4Ib){6Fe{ ;yu;Mg<1QWCjec5E t(kO=HvҖG(VL8`H).nw ϣSyA-3Rde4ƚD^h  ;i=B!P/޴TUVE{=%k{̀g$z/j#H$x hK}A!Ph Ye2\Yr$2R oS8`@^_CiAWUee[UWM7+@Wn#V["f0ʟ xiG!ib;(ѣ-Dյi9xARE B!QH5= [dTp(]L r)j| EllDCN H:J{BC+Ѯ/̔A@gOs%,`d59{|s1ć!+Ibem sAo6.PH[RlHHiV._K:xV(T +nY1kv߉$xzCoGJX*5 {YFd{;)oL Kn]D m2pvM } A] / WD8 -) := (iN^7hfV:Zn=rySz" D/JTGQy`4BTaL<.M\I[ӳ>A{)-z>D7wƆ nv[:4uZ4U]gWY}3u#NއAD`hTܫzg`򁸛ޒH6i PSpPCf@pGuL,Hkqnq.«6|aP^Iy%xz.87L%Ւ9UؿحQ;MK"2tu|PIG3gf[P4&!)aPy920 opG8P޵5v $n#`Jۼ*fk(G_z`lFeQ{CzQEQ8)}N`'PO3r '2<>]O^krya\/p2ep9`lޚ+8r>љ6)~QE<$x̜9#xT6d^qѣG}e:(2 C zSIGܹG)vYg"ens޹Ҝ8rؔԮeCC̄%YsD0ө[Hz ՏP* ,$o`oX_s@#/Ġ[HHi(.>[244$ j/B- nxxQx{)::2`t/-w;>k2"}F}?Q7/?.2~xs$[7$)b޵Yc\I,/){nbPq8`+׏XֽiRԌ-  RZ]ۮk*skRRGZ| @o{A>A V_sr&2$xo} <ܹA6Tr/""xV8Ȑ1 `G |4ol;۔yy_M<l8G|rWBp@E;K: c^) 0QJQk$`!`۲ 4k^`?}I}iVw|_ f3j}=590?+&=;| ыrv*IsY㤩57 P$zAA_x_RiHʒCR$PY8t `-HM&-R({」lWWWU6WS5ԯ;|x}&]vԝYZ;'̧Ÿkj? eqGY_kt`jEEUkjzS9`@Iśtoc[DgtfF,Lm fÕWKݩ]^#Т6 /;{^"ш2^[_Sc2 =fԀ$Դ2/ŕepM4 G aPaڊUq:w3+Vx1v^Ҳ]"&SJW$ȜbGljb%~ǂa|R6TDڎP4#ֻHsqdؑq_b.մQ~ilҋ/*ӊ͛*<}٣$/3Y7`p`W;10"!-jhҰ*b1J0PR2 (kik(&DB:>N$ ~*{AuUEh K2jZP9Ko|nV[|Aig^GP(h8 Cئ zSc Dpl.d$yisÁ8@$*p&}`w9`@w|8@A:T(>R&a떒m A`Hsu^uQGo|oR:La  _T98IXxXGӱS `O4j|ZjX}jZ7y|>eaOh/sj#Պdy__|/L "H=NrHVm ylsspe#@2 Iݦ=,xwst֓?x_ez}睷U|Ơ"՝L֨ ia,^59w6X~E%on)~:}UΝܦ9Iwt9@{B=d}T^qL0^ÌFv EiK M3(rbxDlGF>ĽJG777uRq xܲȟ $%vqϯ{hߒڙT<WzƳ ^mbHBʳ ~t0e8`@`Os ʤ XA$nݾ҉1b Ԡ@Rv7T +"`{722"[BE?5 / J> E2 7sjc BÎ6'?'A]4hWE~^^#V^|\"!u/26wLX@gBNO1 S;@\Dr8*Urժ4boks`/"iJN D7%yJ2Z/](ןzJI#'*5*ɻォt ',%8 ^z9 LOM5|I:ȏ}z7{j@7z@9Tgd`- DW2 {MغEĔj_Z-M1#Ĥ1qlևNP0SG;;[2M<&UǑjSw_]3pC }OG1ũZk rLGe舌-\T9Y[GJ;My} uUOJ~QA`gb8y%c=9iL}=P412*# w.Bp a`n},]fI|7~)qiʮ[R{oXOm1F0&dS>si|,2g  Ɲ gvv,N%jIqԊ 0T5Ae-I<JY`?f +aÞDrN$98ۘz7oIH&6i &,]*ſ+Mm]8(tbI|,F;J* 6ep8`@Kӓ~@_BvbJiKiY9l bņd,KD$Ric6h==ar:QU[OOo[y ͭu/z FRR|;F Gϋ ! :(;[Fjݡ ˵OI)ÂmˌvZ~3 Nj/Aj̢H:earxႪǖy]{rF_/+I*38|5^TqM!䭅LD*YiNn S8pt\TxuU `G]c_#q 1h\FY,NNDT(:`yaY0(#lhրa|q5{.E`!^yʫGf)en\o?h7H3{ ä\QM8YLkvVY} _!jvnV.p:3%Øqwt%煻dh`૕.A3|Spn; tP7-Ax.4 9]VJoMHbTUVF_Es:[|o Fȁվ8WU.w!&r5M 4vFER*m[+ sw4)H D|Z38@:8Hv+R^E6^*,a'C/۳I,> %7>* ߓ5<%y6 L Yꠥ%Y[[S:ʰj,Gzxۼu]ݖ]-ol>srgg~F~vGhx3/cIr9 =ahn`p]խY"&PvA,u^J]S~+prKqʥC+=lju(t钼˰ExCB4~|v;}]\e s d;R{ /"jv'# ^zU_U G:շxIRn'5. Yv{ v(DF ]>X~1qBA8_06Ǒ XN)I a뵂q}5&j]p`8Bb5 ui`3(w𕙍%9.C,s 7`]?12d &7>1&Xߐeptbɉ&q }A!O904-[*;\z<=?5z{#1]\p$8 F\= Bڼ&lCRЋĢj`6N9xL1F]Er¡zK_nm'7n 3Ƅ\pAMfܿFVi`۝y\ZvՑ짥Fϋ)?:sgekkS58q <|pNOyct~xԕ6c$y]ei8(BՒ|fKP: {333 }r`Vu CR!04b݃:`Or<A'\;;>)6:x;99`3Dſ _;xhVns>ĵЩl9ҋ/${$PM~LXrȟiN#I^ CJ8@v=XUExn#K[K In'%s Q/ cKn@}!:Pd:~7t{+Z &kpqSTzpU~ hQy+@r;S%ڬ&mZb]W0`Au)s̋ҽA}q4粲xQ(}xąxaQ`[|fW:A^)(9gpdkÇSsV`wz/#]c66>^rj8kXdgII.} 1T=jCc![?*覇m̸Ţ/o^jK4{.olT- JeṙmښqE&"ɕjqaJ9񈠮X萔.֝R,> KaAc{jcxl:# QH#3<މHIOi>Ԇf(3pže=WLG%=}T^`~_}yX`nrD^夼*TيQO9AtʧE.,ݼh+\,HhkC孯9QZFЧuO浏V; z*`(*œ5ܸz}.DO_5~6?0CEt6Ob}U=(Q'q'{%elECɟM&:mWrmb;S^qMb  F=?>?g d-Nv>[li)&kc)80 O}˶m_l!+G#Ɋ5ʊ\yܒ˗.C*6# ; ,z5/Vq$bq]Tp/W^H#gIjI'&`@|E3@[I6C8@;Bz"/ƢNx/;Ii2,lwsU-c ޒƾyt`FFO=H`KL8ty[2+*T ;19!I8 |hP׾=F0,sՎBr@@:O[OaI#sp ;t6>EPZ@RS aV o#ٕϿv-<M)>ʧq[9om߼*E|Hx<+8oe9ЈN LS 6T+$5xҮfxRQΟ_l+ЫAk_LMM+{;w(b!);RG1k'Cg~aԞd)3SSI\j_{{ pW+PaCzQ p䢺aPTXa ]taU@<گ t@Tө@S7(׵ 8_!fm'̱[oOOWdٖox>|UWUOgzrP_I?~@3CPAif(=Ϸ.gdVkț&"3"29Uq1ܸgmFJyϞZbtx[XQ1d59d0T]w@8`ǵj+.[=k;" P7vޮWkVc rl1S;\ t&zx 3lqܵI䝜L|xiJZ >y-f'pffD"..g_|^?/Tn:\!|Ap+Y^RTg(2z3Д,>#NZnyŃ*Rbͬ½/ԃ_c޽0qfׅQ<Yl1-ZJ@ݴpvRdscS%tRmI3 -tiXZX^[X[;)^̽P)ͮ_!EgZ86u?*̱U%`֜}L C&gR}JAl+,sHm[fb12V?c_BgK;HGXAX& ne% ؐW`wJ7*/~wj1 qG{yweK/IrjZ?K`X^tCVMA0߲ӏK*#!oKT* Xs tpLloK nidT f]оGm/x v6= a)xEĸ 16?)J޿N^cn6iY)Z;M{ilksƪ{z_X/ `Bq4_~`-"2 sjq;A11 ttP_=2@ڼjG?f ao 7f[ X2!y)EJF.C y)Δ  R(1v#OXH8ý(;u]jh*DFK +w4kw^xN<~xpNd[%Y_#c45KfhijZNމ R% tfB ^<0"NhmDEy"F92cl}uѓH)|-yc)#=1+TR x %J}5x7 ( &w`]=Snq@+ɷrie _&Bd7neˈ]̋on9!_Nh`qjQ%pBMzFAӐƠ#h{])iP!~ܼuKD2.np;pN=9FIyttLVWT4"lGek/Vy(|/2冰J)$HK5\d)"s'8ObP&7e&!q3-mRc.hqτ^ݒC77wƋ߭һR:V^.U doߑgdeΛ]i9C nCb/} WGZ!ܸ|5^( oco!5<4 bo^DeX,"j7wH({ 5ԃ qK/2DJI%("0!%:QHJЂqcDbvcåёRn{ ҶA\ s!`:;Sں}XwT[; ?/2l.< >{L*,쉸de9 L `C.Xc F̧2Vqq).0N UX&Q}'sN]U }HF326:E`] @>h qJ5xvZиUN@M_0L"4gwm4RJ_X)Q.K NmLg*"[_^r;o| dPU@ GR9cs[7Y(9Wx/p5ʄ4=#+a ?2,s;/({uC\Npp1X8:TƇRNHy/a~c7c;"lcqoOqzwwG52.<S#{\Q7C;nBgAZФ.gb G2j@,ʍ4|3RI9@9K z|!H_N~>7Y ԰Mj|0Pnah6dQk7jlXv(K}UGZ/Q{CЈa-H~d#絃\k:t~jW/{\l#0%^)īE?r.\vi)CPTdm:!3M,q܂}j8dA>Ө3  fH:;;s .@*@%@P@/+G-LaVn:n+_5}AI!bQ(qhd2(r b3.HJ4ޡe18$͸☆/ '-I  em& opQ'J'TjGFT/Y?3 f&@Q]G|W5~e6¾u6v~j fv{w)z.a͢JHmsn`(`⩆ItS ݇Y/]zI\ZR>DU y;"8_MzH b~~6~6ʬ| y pX qh؍b^Zka:Pץ&kע š#"nQ@ȷ‹ѫ/_<(x&0E94ZfQ7Grqh08޽ё1;-fGuUo -&@ѽS{Z֌, 2kWI  j".fBѦ  „^R)ҧi Vy[2w7emkSnܸ!2f֌Y/XXXV-B?޻իא3gAAB"]_LћY=.ʋ6m"U  !=Ě/T`j8ܼyݔ=j/4;qXC~r^/T뿵@;쥺ښ/cEOЎnk)>ŵWr%X]I}shf@7&8N}ݸ<ッ>D"*‡E^cV:ۦ L,s{g{58<͈x.5쁘sM96sŎyʌKS饠>i}@kHBU~wryZHɁEڒXx3Ai"5C[͓Qq~W};ߕpx[s" PX?vt@U 2_fÍ.7n3cC);󽕦E:bЬۦ֑NI =fB襂kq4wy]^txِyaRȦqb'< 4nE@E R\cs-p2^_\_Gy` 6|$Wq.Z@VE 7^_#H09WoޮdHSWE{Mb1 "se<@"n !2hqT~Ծc%P/룏:|ix"ˊDF[x_K?j׭y T|9Fai%#qXڼ-нA$10'Ɠ:A3٦Z>(ş3!.0.̨D٦fVwwM]s}8Nիgllnʲz E =}BY h׬x\ .r2 "K{(H.v~~^~%#|Q.oǞ;uG/*49١Ew~@սE׎ټ82iyzN+S? hKRֆC`}7Oy1BhY9(!!ȸ*(@_͍7z75,\ΌA ̶ <+] SQ;4{w%F-uvu5rjЪVmldo/-ղ៧`H@G>GKsGhQɂ9+H8yFK݅m7tCJnƅߩ#k\sXk>}Y .Gyк;|7g}'F#NxGEֆtahg/omm `oSӣ  =+|rLW׽?2=3rIemm]'&d f7dwgLޑ@#h;=moOwc͎m#po`& LMJ NWܧ' 5kV}py0IXϡU0H J핰#К"kSx"`L3sFE~<5^ 6bzy{݂xRI ,4yEM\/Lpq8ae̹8i>q݃(}7#L1GepI1L[;Oe?A9͖) ZJV>OZBz cYG޺v=*1sՇ_0wC)Շ;w(sN9Dմ(}a|%kyַLwy<9):|t>{" o'ֆ.'̸頋f$?hwNs|VtP ɖRݙk&!#qD@' " Ȉ,d~Z3BhGp[Eȹnv"Df$"}8mK,Fa dggOx]p]pwІ ]A3e`7+@S(x,!oon@st( Oן@4!PgٵW>CїН>D|n|"n~iTtjmtJƓ|HѪ'._X,eP۹`q`6,2_| *>+}jx햍OK]uʶa@:Ke䨍ֆ⿜{"*.,9rdW[U&ʂ1=^`Ӽcm|3m Sr+ʯ=Z__S)<AѭJ3C? ͷ6\q]WdA&rh2eh{~yZZ0s[^TGS-5yNV|'B9B%"ki-" P۹]XnqNJTi;F%pZ A]3:ܔvqs)o0Q>pAd/dɰ"pd,$N?jTvru0^xQr0)௧@/KPfӦǴ뽱?i +Պŗ_H6u4SR0|cZ۴&'-DžIO{Wݧ%ޏ>{{ XpsOEh\xB|B1::?u[\\qGkj%pI 7>6!~$A_Iܜ2- hǭ  *~|Ç4y9Q}tlEl`=0-NH(ǩEw./- x].=o<"d}k'Gd9"`Fvz.~M˳gT RPÐW$jX5[>NK8볙6S4HłJHӟ.[&b蓓Ro^n^5oK˼[$ˀ&>F`+G[w] x@Ad@/I@!~;5&5$1πh 挲VR1̴i͋߼1̷nyAyJ6@|9zh{ 酿e;e9FSf:@ͷX lTuSE#>0cv#D*uYHʼx K27?05=Vt8/ ԃ:yvO&I fbl ">NJ jwCҽ9 $0o)l}ES.r/85oB HD$Eww egkrbROD>}NŠO-a ¸P|>#wi,ؾ DQ}6f%F {݁{*鈲,v(p67%t?~Tt`Sc;5y햨L"Go9eEL듏G7%8msi# 2>:*9SWlnm{ͧ˗/C#ְxp1 da!ËXPeꫯT0#3`P6"mC"{N-6IvvQv F!K~W\ ׆d`?& ɂHs[[y;73ER,1?QӔnbHİX4jCk+~>+[ p`reU !Ϳ7|𡪛KHNK.>d@ѥ[uWzB=Z=W-)+++i ?ݑ_,z^SJH!7J9W hw׍&+<&֥ sa!Φ 1,@lY;oTm6 Lw֨Za৵zF\+k+( t(idF#"x`!N"\.rõB'=^=uYaeNL%wxeƢj{+:a8-H1fAgZ tyj5KcaUxKR6Oᔀy93޻)|VVry7e>r,,/7BMqbL_ P@ M$K60־Thup^W՘7y߹뷙ǝ;",un Xp@g%OJN;wrhڱE 4OU1PF|jSoOMbaё1ז ! :a" ͼmé;?|'˼yI}?s\ˇ^+,a2!'_-[|h O") 22К=@Ѓޕ[=@\<;aڭ+Vhek{K%EP69l< 녋&y! E. 6%k0MQG]G&j5ј0m(`m{9y ׆%7sd@ Ґ4̴i<(@k%@ xTniv WACVY &ZFR[rBkvu s2 F[5zC~܆*] f)4; rvhJ~QϞS&yh/Q6.}[<1G%H3=nEAR%) ?t*}UvKF&vJ01Mڝ2K~y 1q"7xCx.q|IJAh./Ux/r YbȡkFײ #.yhjӲr}uO;F gh{ |bF1̵yƃ<FӒ#O-J@ku;]:$,eٕ0$ѓ5ɍMHƬXU>Yf#Ed02%*M[;zGGlk?=?HAhA?JpJM#ѥxuX^+#6Qu4nÞTǎ`(7yj_L͈լ3$L5h+? h 1 Lse. DtDU7 HlW %LdG`7,Q{ d΁uc9rՊܳe%`{7U4w߫;ۍTGVAJ}s >X^,u:ޣ<^(pY&@.";.Z ^sOJ@0 tŃ x(k*&9DnZ{ n/}".`-yFW, 2%6'?Jov yٕˠzaV j>a.ݗ,rsG{"׮^C76mgQ Ic`@Dž @>~_8g+K2530%T+ijkOj3c[=ܶim{TH0i<ږH8,;o2.tidIghECs߁O՚/KsW4U*҂Fpa_vyU,mrɹk//d |`?"\[`XLIM hK%p<kxi>TUIk""]>قxBv 29|i>O߁.]׏` hģFī X@Rᇲ =.B|qc1cm$ޥ*|Xi^Z h et )j*Pd#՛*vƬ|77^vs㻺\7v;g)kk )*Rq^*6ߗ.Z'I@$ bڼN ~rY  %s˖1Izр krv(nGEq%;p?noa 囁RJ2-1Q^Dfs[;"\$Њ[9G<}uz_WWn|f_cʲ-jSztBd{&Nt;y08 qf3atyU rBWqeEz# 먡#qv\?ZE_"`e/MGmuɩI}9ny\M_X \ڟ9IDAT8͙`~G}>՟sX_Cp%?|!~S WW\ <@3\J@~9oh3LutW!3ӡaV&+M7oRiFO򘢌=f8n6.2(R"шĢ~lI (nB{肉Ku @HIHj(mP+o(e6_h^ٺR4q1f_>u6|Kqʨ;HEQ\GGC拻CGvvw Mc %=&)T[,{O4׾ל(fQ`:LVߞ= g58>< +hgyQ yr$ X7f$-,}w1Q >d9x=14%R")qKW ĸ22C_ŏdjzJF`Ř9A<Hжn-D0 r?t12:2"-oԒ|{8\+AzMsTy{#0j,LgR sٿmGO5kyMpֹ<qwH\8hg)G4Q(|vpi~u!偟_;] \W 0s{eŇÏ5w|how3~fQ Z̪]6cknQ.h ^wKԂ߅ Q-E-f%A^xBDg~vwe.kTD` ,`cH|42Zv QbxA k[-6u떢/l`b6 l>'̷̊D333*58R6a㦡ftuL0OQ!Aq}r4a֥減>n!z'&h[&'$@]<%^vSm௤A^3ӪٓyR( tRFh>PcPYf.= $#p#[W9(efzFE_F;Ii%E a{,U u =,>X(4p ܯKw$*kWyi+=,USb?eKFsym!\ϩA-6Up,@*qh 4+ 򚕔>o$`{TH@(( sR3U( 0F]VV'4s׮]Sхr$Z" v Zz-7o# *H#Axj}$_ @)xy2hR=󽮖WX\"Nx<ƵĤ,!g Ќgy=7*:?GԂy4z,`S`i}j U"6aM+ovОa UbӧO`uO/sλ(gt18sQ`/+8192]AΤTZ39FV`.b hH¼L2n惡e} F嚺v O=vz?XaNB,/x*q-/w|zeqNߓ u_xGRЄB.U.Z-H@/ 4W2-!. jR`B~C7 W"+;A.Z/o#Z ̰1ФT r@ruYeШ 7phg#oGa )o:* Ijնn#p15~}o_rkϠˁ"F9l}ObnYK;e;D@H f]@3jthAgЌ>TKP9nJBʄ 3 &?vt^Vl.Y/ӣey+ikO!Ƽ$;f`#Qrw.΁f^7_2ߖf۹*<ɵE{EDd0&))3]}m4h43gGQ7F0_0c6KX`$\%F*06)oĭ.KYF+Ţ dkT".]R$o,@+XZ܈eE|CQ,59?@KW';q#JEĠ?^U(hpBR@>Mr{,VR Jm]jG]vz=%pM%0C[)"rG1n@`kX_ZxaR+&ji< h%sr0>9h/QD]b-TbY/zsh]P?Nˉ D]R'^?M&L1E"rd$՟|$~kn^ ꊈZLVh+> B У|F#r3iJۣ?}[ V0P>D (8%D?Gj@b@$gQ{SFG e \] 9qDYy""7 %+LiRa"" ċY3XT.WߖԊ#Y㼤iz/2@ %k@3Jy)|=Z<-LJh6Ûs_e)YpK"/*f#MZ9 iQA$H6h­: *<jn 4p|g(4)e`MC^yy MLH{ʘiɃjO?QmsHA9` 02 ̶hxR[zf,lW (*p@&ǎ|$4yU5ߝYBS- YBїUY J3"BFRTץy 諦yY#D**6i=DKI ^48X+ULg[A[xd*7Fm ZO?TBQ ͳ+'@^yEBvR&GwC_5m`B9{[I[F|Y{eb$m!CFH/.xY>YPu $Չ k7GѢ BeR"%8 t_ _BD._,xu#q rǟU\db5CDv-{L"%&HDz.~p5r5} GD@)hCGm$<;jcx:tox zԟ؉!RVooώ\6vx#ٲά.H`uGx]o\ˣY yY#Qؠ^ Z*qđ՘;`_N[#gmd̘2hXH}Fo|KEBGsY2?z$3ӊXP3ޑĎqK5@Dpʮ N:_j_yvVssGLfiԙ3şyrh{I(30",T[I8NHƍ@1\/Mء5 F^'ZͷkM^Aߠ!A8ɇ`$oS¹=,Uy|J(rZpcpy<"yR2M  졻`hOå~Mx۫av|P=c; mrlCq&kck5m90Kb?(]Cd amtd$߈El tI`h^5/+} K7eXND2`nI4M"J It+ˁG/M#ȳB Sdd/v~'Ȩ$@/OiD1+㶽]O a* \_i<GtdSGػJb<>"| I [d3Jeس@ XDdmjجtJ z̸VG@v,g_-2oߑ u)Ҳ0܋9I%L(W S& zt3^kG`nF# 2O;S'㤆wvmCmWSg|R,O1V~~($YDUl풃];\$`uE{T*Di hM^Ӣpѡ2B;#LwX t} UEe%Ep6"qʍA^*5 lJ|Q.x ay |9BܲS P-6haDrT,/'sc:}VQ)qfe x,OX_@xf oPAYIx7$Żutp+s;Nz_3кfz RiMҷI-+86\Ȫͦs,`xy,$֛o|̶۷_qm7,ګ o;bx?91OmY-U-{%;Z2C@;$5y퐢c($ҢyV}:yff@)!(hGf(#my\T Q7??'Hfی{ME2FFF$I uAmQ@Nd+cZ~F.1Em\ր=<^%2Ws זoht{G\\+6B+c94Uһhx5aU԰KjH,EݩmD$Lò BePo/^\~*Y2U ~/|J *I0o \ɐ1xϖ,);%nqw˸huww%pV hsY%N pAƓ8eF2@&*Rb𨦶p ˣnzq1]M}܄?(LJb?Na)bǔFϽ}BF5ꕫ|n ɻKIkf\,/nSdvx>ج I'dZi_HE~<^gh CN): ADcjLɃ-˅]̤7m{ I ysehB8-p3 H'k*6 rq`ʨӎf︙>%8EXXK"VWASH2}AX`,RM9bGp[TMU8Q("}$@Ӯ8,bQZ $X2RJgZY#N}8 a[@ϬLKŴg+S$ 28%ȷB_ X8 ]]v9)aM*SS%|EJf~ F!.Xۏ0%BHN嘈Ř! ';D*֓M(O{ސ P>4;uZMHeɲu$7"dF*Xu9 `Msa76;:^YR:nen̳ ɥXL&j_i}(ȧg/;)_?@r{=ɒ#p T+ЦblѿRJBy)| ʔ5EV#եK(())3V-6K@_UmNKD C}2<,%@R.]@qLg/TNS^ oNJ,ۋ7$ I;wdk}]ݽ'KK# AwuǖaƝRMwѺ|hI>*Wb5o 7Y.uJ44yH̀]) )M]Y$ՍZ.1H 0*"V$X+ZZqw /@KG$@SymK`$j"zk!*J $Y>X0_}t\bS H$h4..,पIß/pHPhD 搅<~ Mk#֮jrXS,TwQ>?,6OkfڭT8Oekd-NR&6i 99z.k'%pCe@]B\W L/oaz7?cabWQżtҌhʥ^]a@@+rhiw qx2듓2 oD3mrbR|<\g?ôX޽;r-)\І@ pNbv.G`@uqmܕya%cV ½M%l>[l ]tҖE%>sx`Εf׽7osy<y-I:^{ ?3@-+4)IWH.\j^ْH.-]yR| o_G*O__\|HQ+ʌ2ɘIL=ҽgi`ʞfN;b@3'E<^e28%ۘ#aF"zP.}aKr,#r.4Kw=J)Ѵ{h tROy%%%p6 `5R祒 ;7X+5u%bVuݻ'LZGe?2iwr+pQ} PȫܺC0 >v @>U@@ƈ(7e&60ª>r. P e)e%2zQW/  &oWNKeYTெ, Zq]싦 0Y**>b+olkU KAED禑Q r䆡o(+`4.x̰"u/]yY][߸lo>j"C^[l^Jk@*1BWkީwR,|)u%8Ht2vnpFJЦY;=ڠ$}c$z%A^Dw--J2҉iУDH0t  ӧ6PoS "{LV6c)UB<Tޖ~̻I)o4,/_`xDnߺ)O/)ޅr AS,,/]cc~esR YK[ mB I^'D9 lT(,qw }}WI A(n_xiY,Rx)z A&.\A S䙒ZZݗ1 1 i,X+a)gFa+| VDiKY1l}lӁ4mکw,9u Y$*_C c` .-`Nnk84ӅQRJ2)˄fShHxnPBT\}jal ".u5s'r%T'࿋"(e'.nx[E“yP@wzq<4Zݐ膔uZZ]ACmŮ*?9xCw'oDa `v 2*xrz#`hKPn]"MI ;/@r-?D*E۷) Bh* ">|*17LK@WM |qd\eJ7_?d}J^hަI&-qȝ2^ O_|n;m*!#ka8^.ti\ ddiþv0kE2oHǎ-."=@^ Q%v`SkWd˒Oh?6Tj#LNM*hUdZDvf/K@VH$"aпL /UϞIDх]"dH`]I'A[ⱋ,Wn >h:qИApDE܊9 rB7[}.:I]^,6d]@{_$M@~UE=o*pV^Th3}+ h7%%кv>REIiL ٧@@@. x'[ | j 3"aet}]0V@_{Af=7"-(ZfgJexᢪ x@pwg~u;kqHo铧9n4>$C\E<:WU 9˒͖a.a5@sF_3wwD~>oHy%hUq2$!p@~54r qyeMx7łqa"dNhss6 ɚ9}EK$AyI^%%P@B V@P¬!L)Ǣxa""@9{ghT,4zXK*SnpfQerRD4kI߿/vht ye{oÏŹRr>sXK25OҴU 1-/0޺6B;y*ฒ/$kNC FDEJIP7*z=.IïȭQE j07xr} %X(]<Lp*+YǧQ/K`97!ڨW~v zQ` /%>=̓^9g hsݼILpdY U-ErX"N=g&͊~*0Vq/S[eC&0`ru:`ωLW/`i ؐW.\5pYP#}\"[[$ ToWeSF, rY $ȐMˤ*7y+|v WKC U[[}c,iF3{wV2c2kx)_famh`Lw~]/n'1$юwBwb,}P2&͇Y$Ug{+ëG?ֆ6N.ڰYʾ;Ø*]-r$OrN_%SbV^wc @&!o QIY y6 Vuo!c:t']o GwI unjgBWf_doR[k`x]a-c鏙:绘y+c2 ~~w2lf8]3bqkXؽڟyN&{NkfhX@LG>du0Ey> 2byw_pŸۻcٷݍq5@o!?Z L>.-T sYfaq|=*U$ìIz'kϷw-Ֆ2+$a4n.'sKl%k&0ѹĭE[K+i:g۫GLp<|&dra&o Y.' hk:̚Tfx"}׼֨{(!l`w8Nr#˰,\[8?w6c ^N`vvHxKun)cT.ZoRcصxY}bB WM`.m1ն쏻z\9KIl)',ͥVKX=*H%%'-&f|ͲkgڻT>=U9|H(<\˸T>"lxy5n7w~ޱbWg/7b7{U||Vd/^څ,g{h9~+ëvs!_ J*U[J%nSBŜNR[|Uy;I/n xOS ;Fqpuu1A*p8Ύ ]U e- v1 a5d9W[͌ؕaw}k0xl!xvw뻯ƭjeh߸^-@mRŗݽjz5 @UUbp|QeU&1&Lտ⪪wC[c,EAsU˿qͿ`WUYo*m#PW1_[ /ƇݹK@Ȼ^ @[Ӈ0MZSSGqYzm$IMOd={ڝ:p7'w.%@5$}{'~ȋL@LN 0BL\F'A=<#@̄7I @i @3! et @i!o3 @Ly3q @`Z@ț 0BL\F'A=<#@̄7I @i @3! et @i!o3 @Ly3q @`Z@ț 0BL\F'A5>{Bս6 BW  &hdZ@ț,iJ yMy5N# q+Us߀הW @/_xAkM @@g!=Q&[\.ZG @ Lx ] N @Q@?!ҨuDn~K'!a^ .ua)AnvB^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.B^v @ ym}N" ua) @W @.•1+IENDB`smol-2.0.1/assets/images/logo_fullsize_transparent.png000066400000000000000000003525511465701726600232700ustar00rootroot00000000000000PNG  IHDRyy &sRGBeXIfMM*S pHYsMMʍ/iTXtXML:com.adobe.xmp smolrskity transparent 2 132 132 2 2 smolrskity transparent 2 Ң@IDATxٓ]up93oΘ8@,[[mٖ**EEG8+ .u ?`m*IE!"!s޼'q3 $HBf~;<_ƨ$Ԗ<]/_v/]\rVD@D@I}QGy뭷8^cgDۈu_׼fs8NnKg.<{_^"CF?语{_ ]N~Ȃw+"p4hLx\67 kk^lgV+r9suZTD@D@~d @-E@@o&4>` ­l-å!-($V?D@Y#SE ES,t:yA?7Vy^p|Mիl1{?sZ\?8IHED@")TU"pL81nd6,yiYCs0L`]7*8^tVp84pOr^{1@7M)""pd= AݿWZg3nƮ3[ܤ;Ql<ڼ:w!GE]pSľM/v{^f 6\ Px B)@T"p 0]fͬZV#&]so;&73 C܊8Cq㔯Bٝt\g }7笅wJYAED@>)uq]̅a8V4Q[~d8 $sPy_3DȋWO\rSbrzNܗq,&=ƿW_}5= =OH@""p PG?*Ghso)8%H!TaxCe ur~&u8|(~T܊{:]3}n9Xrqׇ.rRxQ8ʄQ6(rFkk<k,l7ð; &\ xj01#E@gy5"LS33"ٽr ܮznP(.ONNth;P`[kS.} 0@pfnu3sӃ#bT,yPY8AgRkޖ̓7-%Ý٬~N/"rD+6V "ղ! KǡkDhp(rf_BĿA`acrۭ /.LϾtrq MUJeLWC\l$.L&˘\&cżp>DsV7Qdqyt!͊A}kRߛz#"@6",{v+u'w%nxϺ63ۜr\\.-e; paPz}LX0^:`ɣomsӴ;mr\.;@50)R%ȴ{Nl=&x H=OHO,>Ú3p沎߂n{: I ңL&r'9Xrp{օ i "+ζ4]e؅,"t!*yțgEB!}Գ "Mqǭ*kާ#P" GDޑ~KV8c|ve sܭ魗?zT>k\v|·`sG( 錱N񶴲b6xDd3Vzy9Io Q/m\3@NQqdQM@"oo.+G7R/ö5X̾aw .ݭp3Jy…"b3tR=4wXBaIQG+]T4%|`3}p ohe-\OsyMbՃ<)Yp"RUU'ƕNt繳/GJ*" "p@= (&G.dAt6K{vzӐ_zl4r :Z0,yc 3UP1B..5FMcFu\vIm>lt=S-p^ں ߺq\g?G>XED@Dd;$"Gŋ33YxP EGl0?7ˋrZV\ 9\NP9lP{ wu9H>W+2uݑ%/Z (&uST Dh0f+3a8k/{gl+*" " ! wJ:Gm>0b:kz f'0sŢyz)UhuL\yFr\0((-\oa֋`ui;D*E0Ax@F5ufblř?y*" "  ?UD@*x^߯x?Ojp]󾂋ބ:U*hVRRspQAY,5uSUV݅"TW 2 @l]b S隵 "Bt# 3vޮ˵)" "v3F"nG }6ܗĶJnuchWQahS*FmUZkm 91ǔd< O46 ݺ< u[QglŶ.KZ 2@8q$涽K{&Ռb\.u&Lǽ}" "p Hǯ?ӸJEۋ==O񳣎??D(D7tBn{Qy/a>Z$psA 5 slTɅY/8,  fw}k/Z(zm+^*gٮ{ BM,Vw/"M3gK@@(}CXϸMhhEXp%?hhr]կ̯?aVV!'fc 몥v4,0.\A_^^_?q.C~+_ѐ*}6lQ8d;OMfey-.-|0KKKt}7W&)Ƭt'˃}-G9?g0y~I+ WZb\ 3vyإњH^;pdIQX.l.ݶ-̤3j-6QGo'9Ymg/Z#A }AΨ")H_\u8= <D7?ӌקu8$WG; 8 :XZN| >NT1{6-[bj1Zw BRbRdT" mwS!6dvoyΰ?!~ ATZ{* si(j-iQ!g^6'H:N16ISao֦F@Nqz03XJu"jͭĜoڝ)qLJ[8~iXko}s0Z? \\ե^cTp:>6 8d;W{< ]j\S8aگMw,&rz5Tki(8~+nt;]}ׯ_µ'\?SʦSJ+8Wvuu59o5/#;8ދ93'^x4\n\2p@ dA?q|ٻ&_=* 3C,Ө l܇VԥȠ75-hOۧ38ZS0U4}.Tc?DMulQDtN9L49{\(7DES Ff1.Ã:eҺQ ,S67]W-WӦAq?STq0r.ۥUl~? GLU9Vuj@#.Z<,h[W]œsZQ輅cn:/ml2Ռߨ4| N4\s@qB af}P8qHyf:}76 OA؈8z\|u9ZXK0.dkSaC|+v /K{lד=;#4vn *ĝ*] i(G9_l3C+RlN"12`82UUq ǯպGy>*D:{< {W{{dQnxg{Ec Ph.e{[lqg/m7}88^B@Qe؇q7 TM&!Ԡe0CMMc} ' r<7vﻫZ-\_X- y#- f!<?0".袥@Lmwjq@vqJW5p.,2-(šg]86VMޯ^5ѵh&ABiyAn-}h hsBm G y wqY\{ bK{2}r&Eks̪[0qX2An5F0d!T<\cևu\Be'pp 0yXZX6@"Rbt}/6=5@mNNeqSdʃk%K)5;,O}ʎs R5~~<Im,yAB“(4!m<7}Xe)YyU:Sk." ǕDq}G߰]pi|sKquXse[(K T)ҥwp6,zqbnp?;7ۉ(0Ypiʣ/u2)Ne( iuJ9wto \?!H@r቙f-c[ Ș.-,d({ 52_"-G]N_|!ֹӝVsp^S$R[ŐbƧ!Gב{ڣ퇢J@">OTTF! mG6nclŘ%!Iij9Jә xf60z.3Aa $jQ ϧ}ͩ`Y;2 -o%J\@ eXFo@0_kyZ&f Pˋ~mvb7u.;6eGXwnewoeV7pyX7 fBwN75?p+a!alfۘeb 2A~P~kmnqr)8?kf@.IDXƘHݽEv{lkw*n)Llsڥ ѻZa*Ξ}b]Yϋ w j7!&`LrөA{de S3 ] oE2 _җs;+&g@s{5=ѺGAә *+V'7 qWL4 V;ns.,䆮y^44\qz [=Cd._Nqnt#15QWGpRAn!RsvIY{AmM4m/'BBQw yu.SD歬mams w5vݟ2R85GxsƽtR6fzmDSNd^ȧ|!tqzC'. t3ߢDC7nct1t5hOP౅x<5`- lḻۭFc ,w2E_y23er/DbȩN@r>LW+Ru*9s{+FBu M IMZ*cueR0"VR)19j[CRFSR`kZNs\yvl>Nەޗ{JD%+?bP<-6$ w,CRb̫c}b y ?N94'!Q9' xq/%;_xsLqZ٢s AtOONjʔZ`Ƕ|[8Z.x6~"NC{0OE$znƫWwoka\BϪ"|}!|{u<;{n*xxcpBōx8.*x|#2bF/d0*&2s[nY7 ͵ec@D\ ,JpfL7pW1SteK@t`xR4lkb 3mq޾"6^\0.?77??m=P{F޾W.r,ZMǢiWN%n3 ]8\_Ļ$^54)_f \ ǟ޴7Zqb~鼺]$+&ֺD%Ɂ'Bb/[2kv֮O6*" g|?dϷ5 pI^),WF/{ hd{/SBJF{Ef P  f9޽s'+NB]SZÁ:2]iUXȆׂ93fu|t.n3i.[Ͱ'h+ݻg#sֺt 4Rpf N{ۃ&R֑n8_ŸC;Yȵ D@檹WplB'wg_M1s!c[2[=?{Le@; vbЕEf]c>9 #  TT:ZB7-Bϵ Wz-)RQEֱ֟%=NƈaZ39AŚA0 -<]A5hF%`$/ᴻLE ŤpZۻchUD@2Vp,5؇xKRΟ:u:[G*Fj—1і0|I8qo`Qn~++5t i.l4r8?D?†Iȸ ,|O q{ҷ|_P}kN&3UU{e*5;0yrp6v/ jyB9;Ty2[uJyƵяp&Li1SxŦͭ`ڄQI33Ecd-(za0ꊍb&QKr`bMT(R.S6y= 'h12q5^gZ\(J {IBd>Qa2{>̾YlaŃc@XDAA{ |Rگ /" D!xHO++w`Mx>p{Րl ߧXC@XȘ2uLOǗ:-)gǗ8_،䘳Egcc6iw]s;vA:}\÷}3P8%vo@1?᏾߹ᾥlkݴKRd.]q/x*._{x W|n+c,];gqCPr)|0*6vTx;\˛ 8AJ"ZIaB!K PblU$A+h6spbVDo"o sEDĄ ` f̝~.l+XЇu⎖N[}BU8uxP7xm =TRKkvݹ6"6!tѰ:DYGJk6]7YC[ PiC ,l9f$w U n B#F@#@ם+W9~!#B' ib\VkWu) '1oD1G*@w&TU2T d&'Ԕ1&*ӂD!0*keCDI\g BSaT7qc__lzon4Ws͘ɨ &,wWs͸7-&.W(Ǿ1b~dV0bf :,q‹)h;܊&Bo li}lAۄ>}bZNB _ȥg0n-R^v|]P!8)x~0%aKwMT=}AR4Un5Z`(xҕ'N:` 5fH3[fɈO8zȗV+sLuiH@Oay&f*t|y3Puet:K+^wwl [01{07E.E;wwl-vw Lpd%ݯ- }ZSq%l%Kڎf(j<)*e1#DN]\{7GQMс%?:8~0\86'S͔3B;puG8g'|\yxj#/ ՑK+c @⥰cD$X. Qbc=*,ǿ?^1[̙Lt oyo"_)@N"1ʖЪDZv|j. BYXřF PڧFHW}橽ѫ9shK9F22lUih71}ن6 Ơ;Vh%Ϻ0ngw؈f虍?t>q>OXجe@}=Psju}Ҏ\CἹLk3m{_{dؼbqw+W*ou8$xאB[3g2'CYxO[K1싙K)z⋛C˖3)]N @"[{_鹼̌ kڈ9n9 #]G5LvRT,rO!MX ~s`pa3U0#giC=w@  -Kyg܇.ÙɌ<1x=ZGZkh#J]:r)¥{-t֬Ci >H_όK9Y(haimbQ8u q[yHkgp]zà/ö׆9u39" NJDޱzIg1缚Fx2)Cw=W11٘nz^/4 6/]&]@z$c~/3_  i.fIE?s挃Iq-i \Qu.vU}?̻_\| ׬?FiKn ;-惭ͦfjrZr`dw7ݜJ.ZSc-|HӂʼnzIJ7(⸠2i&v\8}.]L)(hͤإ品8)@\t&IC8,zfLzٺ0!$|\]}ʺq'!ܶc#J@">GuFB'!aEixv1ySgO,t_rp94RE;&$sYXOĊCi: sh=NZbP~8B)=h|'^Mn5oaW'+H\y0 +y끈Iv^G[Ug´Ü@-gF-/lcXX薦Ya^+ac!.5ݶ;%V=o4D-ѽ#'Z !f^>׺*yq=R˫B,2Li'`McSͩb!g,L.v+62mXdbZrD4B/}-k;ݷc5W16ṸoS7o܃nw窝gZ~gn~c -Wпz1.?m]R)Ic۔l'iSrIO/XqтG$L A|#QM w.5FQ+-F׶ۣm~c3 ZF7:UFӂ]@F pOt ^]QU8mZ *h!y0蠚VA{w}C-*WE@"x=oۯ runƭ WMҨ8?@cs.t"@ܹ{_ozIE2֌c\HN+ ,woP//jNwEA@K=x.$?̥BG"Bܹw(4rv O^DϲUlZ[ dt2^ځgo L?3i"ǹf9)e7Z֒DŽ]UXGa܊c4  >.ڈ'B4[ONJ 6#BQzpIqNaGqVޅύxU`Z V)Uh)H 9!p - E~< EXD2aۏw:~]L}()n83//?5-#L tb /ڸqb ֊+3?ɏMRj`W1Rђ/!{{/<"юB!_xuSq?er-\t[7~/?1Dr5[~C,Ap`36ХԱS}~u M sv3ւR ɨ-X6Z [+,(/ރ%[NmoƪODYHݧLSUt12 E" Fw*\u0 4;G1N@jyM6KЊB0ܿ5XC;H9Ztt/[;xKK2TXWZLS8HbBb?"?lmnbXRglO6kCƻF""9%hA^h#CqES!u" 1 pekqC-s(/ ;0Uc wp2GY<,>OE*y[5-G!YQ렵2>RrIL'S؍?gFN7x¹z!>u#CA?:p ?ڑ CVGóBxRBCiv3QΞ9/_ TD@D%?Gu /$uaQ,HR˝/Z_\X0wn2/Mb14q#SØ4UIyl_xiSOA~=s?:&MSy$\1Zڍb*3iE1#[ۊ;V0MI"Vh$-Yܠu,ylq? T8X8Q1WbUIն~~~FfCBiX8=&p8]0p/(?G1m ȱ=*N˕%T \"! wlΎVj?w/q`l d [ 嘲zageX\XAޅ%#Ha)J˔66+5;oU/e=| |,.N؇芆zHj>5'"#}ۇB1 Ѣql+g`漣a5 BBoeyZ Mm]z#-V(ڳ=fI +$xd:`fL42zz=Qegg wVWy 1V)bN QD$ :Kxc} 9.٤ I jZY9!qɲash1Kѻロ6+m" "p HGwoݺ՛Qwa^owiQ`\]_3SpNNLfiXMqaׅ-Xu˗+n0 خѶD1v\cVGW&KxUr֐ӹ`eYw%sf ǣxJ$toR۪tf0t0Cı/H Mb!|G8)]UD@DiA@ιh٨a \ya0acv.fΝ=gg%ݿ w%3 1a\gpƸxRHlǁ᝾-(h8|ʶ#  ?K7w1y6q0↜x 9Ҝ43is" V BJx^ua[&"1qpNov|ϊmgF0u[ȑ/XqA|awSEm{Ms >D`MˁYx$JKS{1c؊*^|^xYֻԲ)Af,jE X(j(hA-</z̼\.Y8[=[,! 5PBnʩ|ṘYa8wr-b2φc(Ɠ`O:35Ak]j!e#iice;{d~/&o^\|9w"/E6SK8t$#{j 4 od!ޓ-XR[p1r/h 6TY0Y\<1RD0i흻w}fh]Z^/'-ห$ FCVN@1oyQcO~ǵdCG rUŎB&#(dDgNihD-E_lY*b{ݬ@yNR}&x[ w51\yWhћ96f ߌf-s1,y=kvK`)#k.Iku1lǔg֭`:,lݲ6mnmYs1VQDkO%rh9=bTkf)>ͦ9Ud +x%#gf"= !3 㽸A0ybdhH;3p*>IE@zK\a {u:!Y^ t 5DUF_iV`;p n6ܑ++6Io޼i%rq E usBPDPDQDm5i%=iỸ;_fXU͗0Y/P5t?uNwv=nX!/3SK)~ >K۷V"$)t;3𸏹0A46bz'v%##AGYBx-(Rə;_zFV)_9Tq&1sW^wҮ$v3u@D@]r>hY^uB}X/0!G,RW^xqIÙ1l$.DKc8E!Ͻqi‚k Zjx)EG*}L8xfV|c<"-,穵 =vϕ9VYFm&fͼzt1AbXЂoq&xVzsX 8>ޛPvUދ@U:ž:^l˰e)]x#r6-L< S`0cK >Fzp2-X8;^V"Qm '-VD`udK̎Rʄ%|H)3;Z;NbB#OZ6ᢥxdN!,6- M3X0IĈVel1w4Efz&IexZO5!Y ;"c.[^YI;{""p Ȓwc{xgs0ؽwL\n9k#|N¢G9>kxN݅5D;Tsʎ> 2dEz7qطWM8ֵ1³>w+/6f}Cв9ӏ<0ⴂ3^ЪŠ&fȗ/ق%ӠѺI++ʗk}bo*{VDkG0F0&=~U=ψi{<wp}s|^*Do&Ɔƒ'0vmcիWmgz-ԩSWq" "p4H{s0ȗLE9?A0/qƋ4"!}X\8&o /bJ"Gcahqyy*B >*a׸w4i@J`dȘKla L؄pCS˅ ۧWn"#[nXXA b4)4D.ߏiBʥb"`ݞIϵ@bi?geC 3o'i>2E&If]yq۽ Uw9NhIڣsE@D0x`9LV[:Ì}9틃W`5dWYڂsMdBx vt#"xZ)LN,$Sý{VV06R1NF;-ijkCA=/wO?LsEiũ80SƄ ΀u/*r#G2ڝ'Oa]qx`Cv t<>[숚``zK "'N?1wjZĸHM&ș!{kCD"}qÑY @(Ȉpp#O-.i6pntX<@lO1=1S?'- t T}KnQ%VjTv-.Iy;pRfΝ]YњKR,VuD @AWJm%>$KOuzkv)\.u[6^bG:57"Y$Xn]]@dCs_S K21|;Ȧ$^0( mHcFN\dzk~ ̀Zc QHuuɝr6Tа,ew+i<%F|;<.(l>&:fgh>)Ps̾`ӾSz[6Te)z)Wo>tަT.EWČ5]iɺSH6HS??^7kc - YaYC=GxMuNIS]-MnǃԾ@`#ဒ"{I%Kㇻ0p>ZH5+ߑqPEo9)\dտ68< nvmZSEAe >JnX.|fn ݐ#]CdC;NV`3xpz:G}soN,䔛s[ڷ +8cޚ>O&ԛ ?~U5k Pܩj9tm{mb x,C3wJr*i%xoRZbOqw,V> Ssi*H<1P YYr-ާ20;^d|\3%SGȄS[-򠤦kպU 8Ayfvfv&ϻw<䅭v:[@=&m<0mw0-:v:K NY|:e+zs)Hjst{~g{ 1he8gb -TY r<9;sLxJΤ3~PHҩHɣa *=w\TX@SS<[\r[#WޠsF."ߞܴPͼՌA_ғ:ɻЩ6XAT92zC;),'v>p4E=ރ0R+ŝÌaFgȰ]XZBJr鶚F'~"flwtS.뫔+r /*ac&xJ1HFIx~ͯ{q|^d9@lg1=ӗ ~K~2pݵ$(qmřT->] E}ըƿ*;J`P0WPxT,0slU ^c0.b?ˣY *"ӜN~[rc/nbFB-#uz.Z:L#g#qWJ7>?ӿkF{kqܶD΢ vKWvbKmy-mutNV愣S{lߟomhƯc - Y gb=6d;NK sguMYGydݎȲME(,d. QbCn5ϩbٔƲ=ZqbZYI*}[}ʶ4>9`HXNs(sr&sCoU}L\$`$`ȝzRdi F pJzO7Rl\2Jَ:y ):=Kpõ"%`pmT`6o_<$sp! s;4X ~\!}_V^- G.H/q^v7T:9.q\$ _Hpk|s>LJ-[ @ yOپ~AH+iBӺOts FFFp.Yܗn͛UVlZ$˛@`r=2`3I2XqӡW_߳o4#"ȸnm4:X-*'-C֩wk)7l᥆͖ﻫ׮w*yR@KFwj;iaF;Fܵ@~H 4pܥ/6reK\ o=2)uifva)-uw9kn5J# Р307܇|b3(.xGrpI! t=ֱ֛QTxю\Ol=zǐ V=v"K$TbQ;~{g}'zgc"P)!;^[SSSowF9499eꚑMNOdԥH%Uf ?Z":F;ՔΞ![ @l'mşvXkέn'tČTnEݿ%SZM{QNEOAWV%TkQW}y!x~ }yU)hr7=_$)d~s JP]7>u&8nj7BV1nlIаV]R5) Y}=틻wC xmKڠ%Ӏ>( +hUh;v\{{}'y2F^o=x D%0۪xܲEŋO9[P[IYollQ)m&''}MOO{|Z΅D2h7Z[o}mOtyb x,CSvA\QofZ%kVӪ\s)i=#zNmB7}73BEIErEjCFQ< w;eKWm+rM)E1#zTKxLQcGb+[B^o zZ Ԓ|_£poC dU}G 2d>N9Jw1`PLf fMѸw*;2m)77ZYI_>`%V]mlOb{>+rپxf*>0訟J$'ǔ񀗣;ᖲm I^Z:b2GHytrq~KF᱅JȠhr0R^+kz)tj릇kʠռ8WFiqU(@I&qU ^/A=d!eK1~{aLuPMﳻWC=K|vSvgcd|3^[_wPt Zf}imscSP7/'īb -p c;}!e՞m_HёkUh[1hin򥑒v_*dreK|޺n(ZMZEUwWA:{ci*|u֯ʕ[!}0wԈt_un2j v׭)~ ()!) člOL]njcd=3P,MEUenM1 +GSnEGjrN+&# 44(}Z< MҎ+*%xޥf rC׎>s'kr9Z 37ܷU͓1%{|FA8A -q jtMSZ)zPI^KwGߏ$#@l/Y<%ő'/M* /ԍn:PIMJ 'J*S("nve[TnڟGt m+՛ ){/`j)5W͹Ωw\pv7ITpn0\ ljP؎bGlEIR¸;ߎqPܼV^Z^6W#5VsSF)\[pw~\T Q9SIڪpԔʇToYV )#I<{D߃H}4'O#l\/@dJ@ƱIJ"ZE R bhv|@u[$S;qQmKI-__4>?b --+yOSzv%aTjKR8vk{>N:u+G4/7_zYÖ𷦦(Q4yKJں-Zp-x{8A  ˪ K?qښ˧69OqQ(r\UfLD˴-2Ty5=̵k0ܪۦvPHBH.!?TO p?HBZ.O3njIG  XEhKm{m v5ѫMǾ"\rdȢbb\N /iY՟ռ!Ny5[À %Qf|$?5*O#!@l_b"l;8q^>Dž7vSvBpQ>N I*՛g@iWZ4 ؋+BYSz=G-[Z3D).o~sն-5׍y놻Nev*]QS66:*PO[A Ga[Rhxq~t<찇O`kT%@.iBJ`G,{6mh]FٜmFxewM7pǠ<ޗFݤ efn@IDATC(?C3 rzrG#.I/AWUIb8#hջgloV=.&>~pb0a%ee@=NuaP5)5]NB(wdvoBm 4[WW֪Vmf*Hu9;*c x,+yOUxv|@0>kmѢn9M>~k3uiMMMW_}]廥{KR@ +n7C:SXL=~!p?X0[@ {>Mgl $؛j~Me M΅1s-wLe_c7&+G\*Kxoؠ;YPH"-VbVf2f%Qa)Oʘv6x"p9G +ƢU_Il-ڋd4(Կ$A: TGc{lZf)B,1'ҕwGY㶟.T֪;˿?vt9@lOb{30+WttFҼuֺn|O> $Ӛp/to\3ebpunABeDPtM~pZX@--tTCq*%%]%?IYP̈yS!<)m%S,vO%6-[ BC#xwœ<-=P"7QCȴ"&)u*iFϗHpܡ^ ;h: qj9c8,&ōXY8.|>(` bKKw*PKnvVlSNd:۶{ݡǯb -!! ]ֽ5VN)o -ǿ޷JeYg7/*+t;K͚aVd#>bhQ'jȬťz< 0{'c}d$pn߾ʸJWׯhܾ{VG|ʸ_ Ec -T[ ywʕL49@lHC?7m:H ˷}@T= @BV+t PPԀCa)dXQ%DS:aj؆Gٹ*B҄2B-KI\$lLOMFm+q 'ڭ=pk;I\g}Rq@3.؀?+,%e+ wmVTKS(ePEbU@饚8moVhS_8(.:YlwLKnTQߎ9\蹣)j-6EȂcd1%6hUՇ&T^7 gY8AA=s2j a_?PDmeզ=K]#bѬQ^TEøŒYj\X#p Yk~Ht:;u+nŦҾʭJoxȏ|WK/O4?[ @lGh|Yn+O7%j^]CP@{R*^"/4Di5]ܰlG?K T[|?0kɇw{ADj&Pn5d["JQ8_v%;^;5f= nmթ#l%IH1!SuGmHҲ.+%e*+K*%ɳoi_9@Pw#q_ua$zvi\@CTbq Gl&!Ew]s|+C֜CU1gfTQa^z$)}r%iKP}}v0񨾞..kl~C 7fdQnc߯٩/O?sμw}c_-[IX 'a$`혯.N'Umh@7ݓ#i2R;u4X7*a5XMGʬbg vqp9  +^<fE; 8F4PбǰE=pgvGY=pJN?,OF(k2C3#醁[{bq޲x5Uݳ?rr`3b;KCb(<JTR}7R MNMQ; `.P-QJyO=C>IotdTPdӝҟy'%uT &9LSyw\b <) ~|R#[7+͆J};) ?/lblljX2q^1++;5"TRJhP $/j‚զ[1߿>CR 5 ,z(W_oӲ{쫣il(c\S\м&QieN) a,@=Xq'KBl޹SrUS`cE{nEseuYK]x+Y򬳑 ʀ[=s: Oc.-,c%ؽ s:*̼ oK;yom߹c*!r"ůf`V#O p d<+ ]<%_/@}8+$qy۲bAD G"*A/XȴZK#a0JKlb{6ΨY[oxVwO9;[A7ɢb(9RNnhQlM̸Igm}B"իWGW?%y5R"$q,.-ڱ t$1~=@ъ]vmhOvۻ8B@#Z38"p0-%KX_()x];8XT QNX [ulRS}^"2:3tV? *aLb\FAQJ' );7&pf!N0rZ-7nX+ jIa2b}xQ*3(<Ꝺ5. `nj(ip1H<;޽ ~|#1ှWpޒtV_j@v ^o=m\؝V'8=s.5Tsl Đ\ar.'&&Z^yZ$apCUdQB~ҋ/yjfP:FLўȸs-GEtpmmhkbvzVQ2nX'[;ΒaXŎ>pQY9̽!Gg/ǀ eQ:1h4- ;ppr>}#.ha{5(a`k+,/ff3BQZY$ Q hUʛT] .|@В.d<{;Ճk18W.[nj+S^Ú?e/H䵎BL'h??Rw- ݣC OȺ[ @lGo_G_B |+F~*SwWE{n"` XF"SPwPPPRaֲ=Z{DqկL)^nR \ow;2XIPl;5P /`!%ٻ@A w}f=A$GAp!0VbДQ+H~.KԳg7ekaA aМm:c(e yҠ ޟ`n#,J2c.tXzꡲ@ޚ* G1x@\(tؔ]ǃdN;?nnvm40Mg O$.1ޔV \J-UZj^.-o|xPyb <. vOx\狏[@89JɉbC )3E8\mU(>ʜܔ;آ0 /-i?֣(֝j2&1UZS)֪d7~@>\$Kg .OI(20Q!mŧCV~m AeR`_Ը5qcH! 'q[S_COqiJ,YUti v{DegFyzQ紎ɶNjK(c; p( `yN<1trqbgXf#bDpVòg5?\6weR&DBܲr%Mg#73oIdP>zm+~ gpߢh%ſi\p%ŭ.RĎ֣&:!Ѕ (?-95)eyA!1uwm[mqSO 7Bx-GbeIr$`Q߀D64{T@bpRb'{W~@&?"qWz7o]./^4ސ\){MILJmb;ۭfFj: ']LӃD; $ux[l_B Jޗ?)S]w|6_=RY݈SSgejK v]V҃6 C bA"kNjfܜ;wS)3(>qEj)^j/׆xdd4B)$Db Eś7]G$4vF,aJfۮkm]9U'D@׸k77nIAAPzE5ٴbDQ"w4I%'I]:pSVkzK ;e~)icȢh>׉Y[z}xq%)1zYNR!j͠7R:g`xUl_bbܞJ/蔊Ǧ2IԭXxMϞ9MMN+yYI }ܚqKRVLbʏ+{mSX 2@Gbeiv2Y))` : XմE/8B \Bq\Թ2TVreʫ)6n  iHF%e2D[>+$(A |)mCĸ"j=03PJnV؋9y(w1hɼpr-o-,ku~qΞ>md#I&m׽{qDlڗmvTguQvEV=jy~Wn[֨6Xw^W&^b -X Z<#.]W ¨ڞyvwT]7} A:F`@f1qmILvTŽy:H R4S*ab{qQyjT']N/0&(BYSpTn]YU>%_ !Y\>jlc*x PކjEATQPjk0U{_qja'A\BEV׆qs=Br9s~LIGZE hӺ#V&erj:LG=0f0I;9r[lW-&r3SvXM62jv|^oDL鯖j׮->d6@lO1=qN V~0sZ3o\TrVMt@0cSE5-:GBAU1e׷67^Z+jqc?w~3˰$2lt *BTDezPVr.]%-rǙxC)x8nK<^yM@D dG$w![V*w+ m #X#9' W{7v6EYcACMd UdlByoi5fk_}ЂDʪ%rpI! /_yUaժLx. 1x6FwЈu1;;kJwP/R.j]ЗtLSwqt4Ww֖9b <1 ĐLL8^8ɤzzG/~zIFpkVoYݡt 1_.1Fʪ`x|dĢk(]o𷻄.`VV LNA#=Śu"$8:UEmȲ>Q#@*cPƅOͺꟌzڅ[ASzqvڡV߬gu^J4< ;<Aİy<p'0m֩SܠTȄfF,9!9 Xy}Eܭ$DvnUK@(sE6OT ˱( {\ك7loXKT0JyE\niue<: geF]-׭lFs_5NmٝFoK=<,b <: m-#c˗/}]i4F 'TWE^v6TZO,mG\WBws"YF7_0um"w  ,(T)@i?Z/%YA|5)UVk|UviYnm'jz=uw-^.f\WsؼyU1 ڔ-5=4!d2Xd{h.#)4j'qXv8aL% ˦C@>c>.- 4HK+pRjT~xc?K|/;dZu0P4rͮSx^dyx3*{? s Mu3v?WZc0x-[ nf-D,0d7JXIl$㖔xBfʿ2H9-U~s7%d^˥Jmͯ!de=[EfQR @x3+`,f A$jB;@0 Y/uPmɦ4ޢs1uG߁`qWvu+߭ ˼Z+ ;YA_9lcݢn ٤ %#~ df+v^.$Uy Iz%9n ҳ)O'NJ%fͩ`Ouݗx6~e|zR`i%;-wTffn{^7ҷ:Sʴg\sQ Nsvw)X /Ʌ~U\J,7}i3!ڎՃĒ\}ÂQGp E +Rж-ҀC\+z&[T%b8(L%Fqdb͊ Sw70,-|yGWjG3\dY6 F_|W< hl ky+kX )"V7"yFK{v wjmQ;{oA> g.*l ,(P,,|?&u;[㕟fԉ 4'RAbh|hkݫ)z4R $MwnϧT#JWS8T{ddO#ȶTqYPow]zSnxVm$ZRQkЫ]ZWn!4 )y.)חRo{ȱee4DwV]L&vZւlPtxz O-+t{7j3? ޽rrW!{lg1=绂8?++moĻ11ɠ\cVsuxTQ(1idj:V##Ca9?5wA +;0dIdftXXmb+pn+:1GF/ӿD*}QSbH)0p&5d"^8w]0 @e{Jh;E7&7*wӧ./\h_QĘ2G-k@" Z[}fZ;ۑ*2Q=qqx=}Qy0w[(vVvr(lh}"KV*SP;oKE p*\:s8# %&\ZȄkI-j/^w)}4x eK TPܯ*TA.ܻRx$ $t*3uq_8:CNuoVNPG ,[PN.OnjXU@Kkh9KlO1=Acǧk^T:ye{]BŨbS):bcdE/+5%X+;(wI P7h7,P.MJnHZo0qytQZbyCwS:?QDDx&^wp׮]3%ٔ"86]<ZAX32&5_ʾ T`Xʙl R"E++txߡpZ,(=XHؑxU (=s7.[+C19>ntg< |p}*tndb2ߘSdPRm\6䖜 VMC ul.N_"uy#jN۵ дﶂ-+JJXzM(wBT]+>u8|;q%1NCΥx@5 sqoKōtmiS*=OS飯k{{Y0xwڞJRv"<3x^r*1FxwAb i4OqY@uxϹ>{wu{Vy}ur-b8nj.k%wH p' wg\kMYR}}=7_T}ߟ8oߵ  ?[sY%^Ѝ{Q#|eW[>&HIlM{Qݾ[ ýH>Ԩ j<Qx@n׺^H"ƍ[^y{9wɜ\(s,??} ]zU^vnqJsGnl*RdԃCɄ.Gm;CO` Y?")bq?빣 wO+B!; wV<ukCr.V3J;w(3y[: <؍n'DGS;{uŎOZ p@IDAT>S }ktO LJ*ty 79ty;2RȰǃvLҋ/Yb̬k=X}ZO9ceVmlȐT4z`ԒVښ9QVv:/*=-$ +Wսe%%Uܪ~LEF. h#3:?ٺ:|eŕ!R"eEa∕j%[_ [ӄQ=? JȐh3X^w@R 2|%*yqϺev|<}Z:e5jAƺoLnY9z[qy'?gWZů:y0dwܭn떗]Ω DiUoe٧>L3 @3uK*A rVϛJ2IٕĮl% |[9~]_ޞ7'չ;o|`È_-pb;('oK.%%, DM\ԝVOWI(BpIFЄr;EOT;vAʖP(+Bf~5{iEn;׵d2{FAt{Nw<_'W̙VTO}tyne˾ځ[RVݝw݊LkrI=-鏻(jA=_qΟw#A6"r8u9jcM\I6ƊZYorɍeKU<^^uHrղDqٞm| tgXXA<&$m=U@m2J@ɾ9L&E%}r@"SO7->Sl n|g++Wb3C?b `ޯ[UV}K4$A T3+"&n Zj$_Xm^ȳ@p4[v-˾4; oFFŖ+acXpH+HՊ`.x1:h[ 6 7LgNFYxgdEKoh.(xSXN"xy`Wܵ.sʢd/ F5ﹹS7~ܬ,<}TOχkŃhZLuri𝫕\ )!DVmESJ֧i}?/K,s*r2B̥ T,l)*ߒv^KIN~NUk6OeeB{TH$oϕ7!Qy[+@߃/ԛH  ^vG&gg%U'2(Kg.'vhֱ?l%0dѪ~U(td87yЇAXxuǽf~((i防~,T%]|MnֽP`2mGU.}M sZ;)[knV_}uo)7ma >ǹ;97֧i; Y\?\۳N2b\?,9EƳ6w!97Oѭ.qkwVl! t(%#j.>p/ >^{-=已l]YTn&i{V]48-VTfvYQ~RVڱųkXkݓfKnw-[ӿz^v'*-ЋYw%&W/Df~PkL ܼY&.஫į{O 4J;/%@' 6ʉ=ѹlGm;}>,L[RcS6R߽ۮ-.uN''Y|s]-.^d%RHDa(<0vE*K}UC>zeokf xU%motʦ+$D*2`JXi}Ўާ" ?=jRz\ɊS]:# e[Jh/ϫv4a^r=)JLnW0F%1= , m#ڊ\Zɲ C8;Cpo||SR"4'?`I;"EZYv☮S\b@_&kҎ{ ?ؽ &)Zc >{$DwLZ)RL Ci ޽{Œt۹~|M>O*IGaNda},r8"C;:!;U$*VT/}2R^.XLJ_5 #WϝS\wyܜxk8,Ortyzds;G⪸}uw7^jmzv{W% 'Ch)dB!pE E881ۈEQLN'*eR vfQ2UEJInʊ -iq"1k݁D %7FoNq]a_8XQ)Z`CƞK/`R 5ڷ1%MCs߳y_\J?J^7ܝ(`@^C m,|'-nY @^LQ.\.v(bhF.q9kQ~~Ucq+.+E.^UݬIo x[?s}x`""k;Pt"EzUqmb~@(pc(JˢSΈxVD%ZPXf݇pK'k 聳 ,!oMAobYW2Qbޙc0 K\IbhàLB/3;_z64q"t,__#Κ jd(pA7_ο݋ 555ǛzV2^ks#4-|Q>ECG`UJCPɲ $.qJ xB re`ɼM@rj׶{^g[EI॑ĘrzMi}/9N) ?];a1?9,bf1 q,ВQA4:VW̟ݠWΒNˌo&%6_ʉtAjWpMB8޽ ߞS,{C2,h|cTsIl\t_,A'9n@F ?"+`E9W5_f<揜ё;3v:ZPL0`LX g1Q)F 1 X/#bP 4q"ۡYG>޹f O1LƘ)-cS%BE3q}1>:$øorvYD$"HG .f ij-A0tYP@TJ=&v(@X#G{GHЗ+J́w aF}ʇsC ^Lpb2|ꉶ۷Ukd; 3y f!J P$'{FbTБFptTh1yJPбc h;:Pܺ}K9fkla}Zɣ<a5EX[Bb^ۆ65.pn~/5,ͩPttq3 Q@(o8gGDbfT4DgYL-uuG0/.sw%+(z ٫3a>^E.5hG"ķZQ_e1ƘLbcVBCp|MiSrr\B+i%}wa[<9k&žl 2mm7YҰ`n\pCPŸRtt]]*o!_P&027/z f{GE\SZ|"׻ni(@zN'v9{ ǃN/.^h;1 T8{ZՍ{`q5,s>o$}4+8{{ SR=$)z[c0*xH{{EPŅ:qm8CrQ&FQ-Ӫf׫ũSw EFA-(/ ^b[F-pZ`զo2CqabdlaBq^ub vr=YLjvqqICGFG(-Jyg\!-ϋ +w{@WӍ~ =džxT=r_{Dw" )ن8'PqF1K )A sT'KgkI7n>Ռ:g 5\G]74(P>#a:)y,801FpQ֋:uQ,`)Nҩ#kjJwxr\YЏAdS{*dA As6kKOҿ?=*x9ĹL[k:y}l7Ev΂Jn,E“J1SЌA;u8XǾ#o /;:n]U)5fup6BG ēkǣmmmNp|?YG-ڋiWM-'7h4u@gG@c F:R"?yCg}V<ݲQ1Qzy8CjW=wޣOz؆Fy}9^.٨hko.\TZU(::tWkTtn[ˋUxçgcuU ȫcA.a:-!#ă7'bx/#9{gm|~ $lTiJm{ڹ)꿫{ϰl@6 9gϟ a &P8DkHynnVQa5.\ `M@GN }q\ItyֆnQ([AuO׊K<:?W._VYCڳW ܸIߣHU YpaăaXƎ).-Ǐpb_r^{1"`nR'Rm娂*魦Xqϫ t_~ PւJMV9A`C.U%alUO_qTp4HֱvܠȂmZEt>NvC F9~WXVnWtRu+j/ f[ฒ+K?~@WQ*e8 i|F5CL 8E8fqz1?d)yS- BKyu W.4b  q̥ݻ3gOˁzHV~|E{>-Y ql' DŢW\V(v! Xd(s\QXA }Q, 8}n&ݪju'S%XSL''>(e婃1'($wnbƇ֥.o[|*'|"z*Bb9V5+@|\)u)dt5 Q?OM=+QZҒVtÈ%Nϋ /()[Gq5B6qcI@T Ա>_e^?WJ bf& Y^C,z fxaYXr9Rx\-[L > ȫGȵpܐwx׾Estni1s\ E Eg l+7+Ӌ<{M1Zy'$W_rNJ)^O!v;fuetLHFF)n߽#zb2և7?T !M3ߦhM\j0Z#=%5!i YFzqzDb 8\p\: `LsW 3OiUoDEދjTR[Xy61ϓ"yYX""lb֚V aQlLӰNg=0 2E1R*q_jX\45vE`~ >rSyx@U^19&nׂD.ySu1Fܿڕd'Ey\F Ax0,J3ZNW1Gc5m8[ɹt:\PϫX T,_vM<3j3I;Doa6LjrA+?qN)掻q[y~2ªcu `LNIΈRWpp|!p5=!A3y!|K|]u$8{ְ\v*7Ke |M:q W+3 \d IuEj8LcH??μoο=1p(%XHp9+@w\bKZ cjЧ)nx,~km}},?w?+ W9 iJ`o$Aӧ!A>«͋S8N$2+En^sst"xHr2zEXVZrb'߃QzIgc&hzq,q vtYn  y@8=qM8-y8Sǫ։|_Nr\>"͚w> F,QWݠO3nr~ }`d$tr xme_7n+$Cy3%U(e؞mXQC46*ѓ*‚WUu #Ыf= csUm~שVyjjRES\PŇ^&ZR7|'Pk&^y8qpEp# Fm-0"`"RS}tjLv!CsJ1mKZBJ7 Ӡ8zpbMVt^M uNUNJ ~,_;ყ 1#ķy΂7(aw: BN/DbH 1J?l?JpG G΋{ =x\nãu$D/]M4[Hqcv|j s-2ZK S^bLw%%CQ ?19!nߺ%>}KY'ӊ: #Sз ˈOۣZ'Pxشށ+?h⑰dq:\ūh9N ߂Qi :bq E!q-x:L)H+[o/ p#AwcFo:jzLOI򞒀^ 8aC8^MIF岊DM+܍КbFG+VpN=mGV7An~&#!y œEJǔC_{\[vӠoCpd"2Do1A ,X] %NxxU2r WEӂ.0s 9'x* W3TANѠ߃.3v>fwb x9yLիyz!"G"w£<2xsұcZ~G^k6@o/3S(`@ޓ(d>$_(v&Iej36o\h VG@GmDwmHObtL[Z[,8Zƪ]UnvǼc maPe0F)}KZ2gO>%?!^җLiSh\;h-Ww7Ş\lHGw&/ Pء]=%[>[o'5#B +I9L=!dnp#Oa<7{W=6yl'ӖS^>vsѠjPJ,-t&+}]p,tݻwQg@pnf@\7-]t>ݒSGB arAϐ`bRu]osxb㷼b{[0 VG8&xn-_Wr&S"}7MrBzNr ?uRC4Ȥ'}C8hd7^=ice2s SGË1&X20> *-hի*E/"+HKϯШ mXȥuNpN86NPӸEm'T\VqviEI[XZ!/tK%tkG.I|"+ӈ3S˕쮒rV/̶zbUR0@%GǾhLW\˪V7H%dA]ě7oB?(ҨG1DاNŠJfCRɹv3bJ$4Wմˁ:/-̮ZdCjo yùVcϩwѓAL1%8z",4ƒ(Dq_ j#硗i1=*JrTznِիg t5w"H( /Xccˉ+ i0u#ǧ$TBͼHpinkU_o?r>~<%FFFLyգ@:AF!o)$ԋ.wUnS0V-li;Zy'Pyv7`bFȑPg![7n;{g=>Fx7[xP9.%<9; m>]b]53h-7 F̴W]p9]=]@}6p|--q2NQUOc :O.Gs4Y5ekz炰Zj>cN.!K ;SqWN^-[T[Ҕܻ,t%?B( G6>hˋ|w#AKw x34x /^Az+R1PGF5,}s<ș)$ $/fӷbԍ s(0 QFʴ<ϝ]t,dqv p#Ng-@lF OY2)VqrWh&p4VႀTٸ, 2Wmq/, Ty\W]6h:8"U,u  Jϐ?x?P6q/}K BV@覷Ow]LWo%u񆿉@HS +~_`S4DY!.xcY~^r2xÊ^W|Zz!g)I~YXS!ʿ\M>\@L=y{BVS~Q`jj~gNr*[1H@mBөw+60h"`nVb0{ (j۽삍<ph> Fa}j5VnШ){Xn%>[:3{G9v4 EPDKeơEoݯIezZH:}*x9?+97U䊕fE6< r>hx;!^A_uplZs:x\Ixnd<) \HfIT}Nf7 o6hŶrz((_-<[8vl67`1n!L$Z e (rS-1N<Σ΢ׂ6ޯT(ErU=z]w 3X\,S {!==nĢ5EgϞ;'+.-؟jZn0\|Qub*5׹V<9*<{,J{1Rl.pS#C cY1b #K7KfӭdSL28h-ٜ}jk0zD` <=us~aD Jg2puMo. Y-ӄ>sX;,O_y1<|jP<d W{r .)n4L+c1>1,hуgp[/ xLT|8聮T:,śl` !шI TTyܺEU16FEAF88oALLuUܺUU "qKKbaqr2+ =CCC ; f=嶆Ω{`Pqiq3]]. T +[0@ُiqDx?VFObZy%qŋS9VH*qMZW}nFR֣og~Fx/5ݨs "[ڇ.]LJǛɏoa> /SYȗ CPW`Fo,<Ϥ+So 0 k0tۛ {&.`aOd& &Tzbm0(r?TPNjx2#qVɑB^4h[ur?р K~,%9e{A=@AʉyCѶ<"@IDATjxRCܠyfu[auj4 %GWK˜"ǵkCtxiG-o7}P뱿N"Rw8ث?e\w-xF48Qm{gi?WZ<ꅦ!]ʉ&C3P(#\3LD N7n87o~G~ Ò~B2k'1=ÜۉbD1iDNW~S_ wA:~gÇbiqIYҸ!*@T 6n}bQDA[g7lȈQ 3?cжΞ'N">oDeFyCqmX}X)bRg(BYjKc,޴}?ε%DXcXCƪ"AփᒮӬ]?+aĂ|{9IEp)ˉGEێ:R1O4uS'ӥ{k(s sڙ;܂CadLӶ7~Ė[2*Q*9lYQ'[ "VC<5Py-@IqS8 |Q:pcR:mXヰg=ckniV\1 knc)j6Tz[\Fr4 hXԤʿy.M{7.#ObǼI&\t{zL-E뵭dW>eq4dh z7,3?w˽Gw v>Uxƒx6o*Ǯq1Eǀ}&n(@ދ/huvfd"a'¤(;!3TϠ5$nOo ܱ kmi_d2Q]5ƨ @Qss KLw&BFP\1c϶ܽKJ4" "e6Xƞ뺿UԮ>LtВvC^"zQKLD8ep{r~0i J!|@tmx"K`لWЋgG&KL c|fg83(an9'|̋dda-zS%'}Y J$`єX>'?t"D2-#dç]pk;+c݋BlYB,$ ?AAZ[E,S"Oϰ_{wX$Dm_/ix`-ds0(F!F8ᣇ'''bNUD.C?b&5!Ɏm}1Ǖo<jZ[jmv[sݯ,Y#=_LjG:d vK!mgzuu;kˡ8_r#\}=e'ReZH]|PvD2ŵFN&=ڴh>eF0M28@جRdG԰b%+bDQހ樂v7emO'7&gd5 nU/@;hjh7_Eʩt> $7:}Je u 10uk"AǡZ AF'4}?Z2>42EtRLe R<˾)Y+"W+qpv5EZHt{=QuN2A'Yp,m_1p<>'<99^T0~Ki=JNXVmos1Yexyn~9 ]W63b959D 8UVpݩ@4!0)f] C"H@ ? a :RyreQ$`D.ԔΊ Gn; Tx1PvV(/0F\Ztn7wUפA|+ZN bցvo& ) L oE kY# X !0Tcr4n@4h |c"Ȫ 7F@,uG1-d,C8uvv֌EO )pɂbrhA"B^kBx&\BΆ8e bWp::N "~%T r3(ReJCl{MT|Jh57OGؽw Q*超"h1q>\ŝ0ejwŲ ";(%@J⋃B~9rYNՋ3vy˯v/73P`0 o2'_Ww\$4@(:{lvqcķ}ZZ%{:JٖlZZSr<3 F-Z2A 'uZp--1mPŧ%,×u#NG`hWKff^lXoi(9EA'X+W棏_~VY!l'fE+ ɭcciXmPnG׊Ǯ+r͵}8t<*I;z(S;u3r)hjG +8)!fҖ(o :{G V3{iVr N\e@]?K>(C`'7{V1daAlJe5IxAX3{ԏ#4juh E:T H 7 .c8;Fn||LG6p [CF,ëWK. T6鯏|e翊HV;!6xw,7ʬm9~ '?-jTRuO&fb{7e=@%y|-{)-7x ET>;S^> CQ{#oEVf%Bt9 o ort.5+]-.M@9?ܪK0P YZZj~Z9J ڲ2ZOW7ܼ2`1:e[1ru \oO"O Rxҥ+JL׫8X71H]c ;R,K`ǟʫY$nZ!ڍrYNuRcRkf Ъ{iDX?smSR4$Č1-y,~ oUQ|75ObSVmn?"0hmQ[q`ZC|_@V1 G.`^XE^Ĵ QߖAY5EʎP8²!R$oƐCX0p\mGCrVLBqj~q3*&.NqB8{i3?3'SMd"d&.6yh `:hy\ M@f:~1({DH8BήZ|{% wi*`R't쟅^Q1rn^m]40FhaگT8s[s?q;Myy E2En  ez_VcRoD׏eٱ]]qӜ2Ƒ=yb@1f- ź=PܾsGC~%|x# ETR]lV0ASh49t)m "Zluv{B҅{ PeyMݣo#2{V(nTa;maԙgO-yXJ7Fc ʋ/$(*b֓'O*i 7bhx(Ν=/ז.Y訙n[NT^ )FNuvKGWbܑkGa%b2ö_h-C=!GESyW:c":xuOcrmF?GFhfquL{vJ)}."p,E4/o3;J)7[S*YN*;@wqsb i-s0坜@43sCn^ !q?@ݜ0.-t b@Q,ݱбth(hdG@8\'`hUT]Om.ORzZȇaj,D3)~\g @z$|(8|W1 g᠏酡N(bźq.Y`It=M@ [7_|\˕2jtPLx@C ցūtHG⼡^ }{蕇([y k5[s)VmQ{U'멮w/S7zO3i(3.TwŌ7p.p+f?1ISp֧9k(4к| v]o"ҙN6rEo_ήa@ @=ŭ -k]NBY9FP25pp.VX0)W9kO8O@渚x239^ 3m*uĄV_q\.y-k$#BB"mؘd(J7kϛ#CCݡlҥK70j < ^;I脘ֲe~US>cԒG $ȃ[>ןDD?@Lo5jͶ[g/VlUQFrCsG5';\|8,;ElxNW#,~DJd(. 'o]Kp.I}T&/r+ >,/IQ-p("hL@Nn. D\7N]cjx\2u^]>_[6EĢxĵs$gf=¶O=?no)`8yMQS6)Y.`*E'こ=LO}_5f%fJ;m˺v:\9œ` BWٌ|9i;Pmj27p h$y& [Cs`(p@p䧨YHNa:#sG_m5ة=c6O,[e ϻ֫_V}g6) B>7n lT}c'wiisqg#Ni[`ܼW]h@  ȫ!948 `."GkVT q@[Ywj)X t#[{Mծ[NTmtη܋z=;/)~w!{@1mu8b&-B8<I$G !-z{{ Ʀ07hX 0rǏ܂ 'X?\&?=L |t©uҗݮ3X7ӲU}-Z^[f{ϺÆ"BNχ>/S֓)׻9 lXX/ިG)ُia`{d !I0 ofP ( [_߫X| hrJv_]mmyur5ߪvFwUB6mʱlIOI0#*IO>>1ێ3=bb/坋1{u^Dqgx엾vy%VWC=)C\H4El նZC5Y ջ4[^_͹vy藏 XuT]!$3).Dȉw(#tldev  3R֭[X0fE"V.fY{ŕ,hPVVLާĬq"y\j *t{vϵ}ڍ27-H%.7/l\t)@Gj}|Iz3;W2;&#eˎ]o?dh dZyH)JBhaCLs:K HjcX= 0@Zέý^kPw4~E!iQ;muj%#;.+'Lkw M•#>#Kɴ1|h@Q}@*Dx>o~X*n$'%g T 4XA6p[sSC7mD_HͶnnf?!XnGdD"%ŅU].ھ]A8k@h_h}x:TFX2"^DUN_a Ղd.{׍b 16|g5kR\; +ͽv?&24DgDZNi#aS–wvmG-  (:ޒsr>\N$Ա.fUwvIj|/qB.ޗ*C%\|{YjзW/p,EǗ)! rnU`o7e axО4 < +Z|"FO*NCd#TmTfsWgS|:'}:x 9(~'<=U5{\)@8\m%0-AظOcD[&h@ޡ^ӹ:x<NI$Ă]%2:$2S 8lp_ۿm-vHhzλW@UQ7#*{pPgN'}t)`@Lt̚Je%^ȕ|^ְ[Mw@ooG""=-߃[zxrz۩Q+zR:W w]Yq-j2: l}9t]7288 >ۗ̌a%mt|^yn@f y k[FLcUXwkmr' e| |4&f5K8 IGfY8ʣo~xcR4S 㑡m2v q-?^xF.EL@XӖ2;)SK׫vksox\ l -㽇 ?MpV@1+Jo[k1G6 @Tk-//dXFyJɑVMȎVܼPi횋`c ETFT7Fl{;z۹'i!ɠW"K4zI:_6Ecesdwdt( oܸtx~vuyv6#Er R vK[ vQ61FPYRCtx\ ?6VD;A6]/ ٟY6k flfƛI^{ ;yt:kٲrݖkGC 킅`mN~[!Ӱ"ra&(@t#iSm0pk KAXe+5ޘ7>|I>,` $8|M@`+ۭ&jG*}<!I[u?_{ tї5 - i+SXpf,is4`nlit~n;B b[]kI<9W:V8FޔJp(g:e(PpBme}B) ?/$’1Q;IpJŒH[n!PʽHG{qfgw)@)Z|5!v[+˶Vsm GP疜<8DU.af`xlٱäDh P\19.a~^JGo+v9wRV<`<[)o'y+N x=O(]KB.ރo;_h_v՜<~ąh ߉fPxK%L4)(`@Sj(S / )"&`L=d' n+uQOmӋ ~60VM}g2XsXݧJM5J!W=tM8YGOk!΄a #_Zwt)`@a]ӷ@ HX^V,gNp. t*%̶/GZXKA~"~{rtD ,+dף;M Cn{ĶHY/IA+k15c 8g^ǥM.N D+VAAίmdæ5r7Ԯ\+ܐeXC]!>.Hfᝯ܈-z;-RGWΘ xc0˦-o՘ LxL༅N0VVWٶRș#j"Sd& ``MbEg2ZRLkB!h*[3[\ sL ^MZEev=  ;*#mYw(Kd9Q8:r.DB8y !rE 527۾'CYnizTe ; 4=}oX06"E K"*_*⁔32|NB:& wF.)lʬ ^bZ,Zق`(N" xoM`oIsИ(5]!BջB8>3 pcC&wdVF2-/Ex3(Pf8 ]6}4 $n'^׆ G[>[xJI[ =<h Qs09cv6͔m-xL>%z,ɣʖOJ,VlJL;~Dԉn8@攼ήFFZ2Ғ͔G73OlwdgCA^CML=q ϲlWİ\niHYdR2%SIA&`9.g4?#PӤjJb@.}%Y^3-M퀱#G g.HO!/2vw/S~\mL"64jB=va!d@4Sa,T|C$u-\/VBc>ڍb?'҅n88wGwB)K2%iKjXI___Z%+OEl]Zר3U--3ke #k#ӤC5D 查&% [, y<>_m+ι6J~f4Wh R\csN3X]<rMp[..K[{<9"k^ ϖ^]ris5ȫI$6`gnaD[DS.Hӻv}mMO)7 vM m8on~]+WkTM݁9^T+׃4JɿZ{b<%}018e#uOxipzrk|4y ˦۽vEqD7ԵX^MY\XX4 9{S}8ّ9ڃt7 -3rTin&|矊!^۩I^X{O+\fqb6u@E/Fͦ@Li ;ȩKr@FY=!;Re/MgM'K$Q9mCR u7dfΪk{h>(~~rSww1/N*PO @Ezˡ)xk>OzMl j\Ӷa7I߇>^8/e4āYw6x:ln,k+{;O BIWgm|AO}IIM ^&wy[(ilEq.9qbX\<';Β<#y5rr*sSXfxؚv`@ba<-@agN_lᘫtZwrjKT$ȮAe;H:)vssR}j VS9D`?W΄t/"Sk)y|3. V!..bsZX9|ʊ϶260?;55)_.hT<^R3L%RK'5&;1!  Nekָ;b^X˗Џ/%b PF#S]|Iexhv./lKQOB+֦W PN9@i޴PryIlJD674lb!A^۲G5e)y ڲBv׻v#e;8Mh g}D4ݙevԝYJ;}/Ԟ Z?L-:;;f 05x44ļ:/KFy6z^c YC<+^% m.hQ].sI,^,~eн@!ߪ.9@ ^:&xřԀ{zJM{/xuUmOJ/4#쇉À)%hXfp vy:J^v5!Ԃx=О؛x m%Td"؍w r#sq8]ͯlk[+bEdaa!1`="~5 d逊ȱ#T;mrgbwb\b?7^V:=2Gܼޔp»6MN&龯Kq\9P/@`k ^܎YVmaa!HZQxJ4 p9vrO) V 65`s9e/HΟ?h$ HRGeT"n+-OEm|o r=xA#_KKL,ڌXz1ncT&BВ 1R79p60ĞO UÐOh:>@ղ/h{T.m4%v:sx|SruP_ O?#s7`{bU5HA4Rm' [LWzeIe.؈n9A^N&8' Y%cтޞ;ޔPh'+FJ\IXxS̊{-\<&qb4u?g2A?O95'p BBa|;.1moBc,D}\9Pk8wzϸ =?zztaii<奐]"ʙg:\Ī =)xWmʯ333*=o? $ o_oӯc= L޴+ uCjd 1\|ߥ9HC!½{Hv8nDIro^z7t,]Yo?iI^͘a9`I 0=ݝW[Xq¡Ke0AE<(xA[[ڔʶE/ R>AxHgp)E9/;w͇h}^ CO6^Slweg!&ω Y-xgRLx?wBJv"U!m*YHFL3q_)cjܔh+H3tq!҈Muc}`/}Rx<%qicJy_ToqY^l n^VWQ@IDATR1Gψqu Z''T>bUhf= I{iiBAgy eqe K#q@FM=@Pڳt I?QYZ Ф.T ZJ[cy-I,0S\ M[ʷ8єIΟ.,U׎).ݰ-&ȣg'𰝛g.ë.N"(nОV9}ڡr@CeLs`wIs GLCm1ijj۷63kUi|G}i@I'} )bo+Էt`Fֶviko{*J#ּc_׫}esiXO!/l{.f`d7cnS_}}%X7- g\b"eOR힠'vFSv 9~7ge!Ƿh7#pSs$alנ%p9 Bv{s@c& _<ݠH򦧧% ^Y0ǐS)@UM!ޅA+   BiKBB/|AnܸJuޡ$t`ݭ8v^bWbræH+yhƋOؔ؝XAׂGlʹ\x^l"5[2N~e7Ma].$ +t"95:cyRG-1f߬?5G^~EO>XD<\) T1~.%yf<<SKƊ=(uӽ݉3zVJ>YT`)cu>WL&RH<Ƴ.KRIgyOZ.]Vm]%{Hց.\PcLfy@`[0`'I޽|NmQqȜ{ä\f_ g絍p=8D>ɓ}ْ5v-5ȅ'A&RP86ԋPOlW^K #zSeݍsyo/n9w$x^a"g'@!uh)Z*6lce|<:\Pr3LN#SFnԿ\v>Zμ\o<ޡ9Ptl=b+/Z8Y5*e=ZԽlz{WV.,.ƤIYsSSS?-L5G&Hs8@)Stx*(J91pll)=-]x{7dqaA׌@xH ikŷ\| ^B|3t!* BpЎYmʼnk}85ow>ڙn[1TMPsg'~Kc@CKˣR!WZ&'x/áyd#YۓV-4aӨH-gQ,>\# X\jJieiiu5L͡r6ԜKpr4%o[::R(-}C@{tXL2dk eµ-ݼO%~ثj|MfXK(7ғ=$@  sr_KxlҬb~~AStuuxll(kϴcYc:zqU+^ŊidVD;233a9MP\#+rň޳ϟW-tٳgUPdJ+Y^mp|]\mEbHa2?$vSɿ?;Ǘs?^lq |Ad 6Df|b|ѐU6ӫA^Y#xuQ%˷׊:һOUvoLwF@ӝ˽WOGZ m𖱈PzwG@gjl>JoSW@\Yu{ ȴſ,NSe>].w P~Ԩ$JK, gvkV~ڳV:Gs~8@vB&C`nd,)'NT`1J-jA A_k.H.wƐKFGGUdP.+&9畻9Urkٜ$Jr@l/ .JO |f ƺaq1쩬i t՝gV4+18@ݩJ(~8{dr}~n>z 3vGVVa*8rlxj|`6ʨ)P ʆ!|Ё[3X!R؟{b< />--|I2hJ$={IN `#} w̫m~tiTh>=PDٮ?QqbK\+K htzttt-CȖ"|J8V %e ().Fcf6l4/ o޼%H"^xst+p$RtEs`["5Txi'X M4 c{Sīxuِ +pU~emoΎ+iK:~ T|TV*O RM4L-H' UH,ܠ3N#\앗?-P^H2#F4A3);s<.s|jo)t`TJ1b(p(k\īM)ch*aA?(I8p1:bXNbX>S̭@ mݨmEBhWZyK(ASVJ߸r 8`o"muuugΜ;cܥy9 ˟/R)jQl,^]];W>?˭A7[#nX_Ac.Z,6dsR?"FDYZڂz|av7ߎxy}Kyٲ}0izfL@Y4ZO=Tyl <48IKzzV%.jqړxy##rpmҐђgA!~6\՞gcݶo7Aށ 1ܰTeˬ&MSA YEUxזwš5=5:m!beU8* _y+:fpT280 a&Cp9N;$ ȩSE3rS }Kb=e@a@أR+v@{t_=n׉[*YCG S* vm>Cv^^Ɛpέ֖ wYWQٮ)dB%-\ڮ]>TN]́jfo, }AV C^m~ -'D>Lհ@=q%vp1ujVX9tj <ޣY]k׹ͽrtYJAND6,Cm˸71Q~G;Q^H,xW|g ɣ(>Z#hߞ綷UM.n?xٹ &q9qۊ<,cZl+j߽;M=_хs7s+Zyx~&l|MĢb*<%DZu@Ӷ9ۗafy)YF]b_+"(FrұDi8J\=lU܇ mܹ#?+_E2ǻWNKMdب|Z/_>jzj^k] AYOoWBAK->, GX*ǐ!^:bJbzfZ910 rya)j -enlb .8^,#68@;OAzp k JT9\s )%d(/9r4T#LkI^O$^Ϡ@Fnå۲Sg+C`.eHԕ)0JjYm䀀 8[D"lW>E@ۃj;BiApX/}ɢZCz>Xxisbg_ ەhJ/ =bH9oAu ݅׾b2[tZ>lfQ@mrƃO Łt"X٨m]tv@Ϣ]M7lR<9"XA9br&7W NNM暚%JV~ \gW @>/-abz]{KX#=k4#URrݎ$EeD2Ե sN´ϵb EPwvy%H-c K:&qQ™1xwРޭ9pTw- T25^]]I07dumM޽+axٖSr E_4icc بutRR؊E)ą._@C0}@c5T?/֧دE!( ==tNRۢt=N)XxŻ.5vizztT88\. Qn׼!ꏚ΁<l&JƒvڵkS#O<>}FTz::x,38oGǦTךPoqA C5{EywmF-Mb쫽-.oXO5_Sk*I W~vX _R*Uʹ/ y%Um$dW]Xҡr#j^FKgb?~!VO_:vjVz}{֝xsJ9r5d ,flJ&γ˿]\XlU@S<2%sʦb "/rrQaȆwUJӣg$K+g!J:~A&^1>bdGC$ʶ ī©O *9S8/M;JȆrP̘w[ hFCSۡMaY5$/S#h4l;Noy}>x΄≸ 4 xߪpO|Oũv FZJ ۔3P3!!7t!C%խE[/ R](c~EOې}U쳿"v3|@N)3t*@Y C%z#l~Mۀڳ.߾(sAO[q6$/mLۥ@pl >11lxvE%1WR8fVcoffFz Ts( ;Bzzڎ*w@%:Ίy{ZWkA3^ cbC k{#jZ:*rIscb0 /]qHׇ}޵N&S i;[GlOv@o){>42Ka)c9oYJr꿚5ǁD,J%B 6$oW^t$eT*-g9::&- ȥq& V%so-܄U !mGqa eۈd?w8{+"Y>t) P Gb]9P?H2Ѷٌ5rp(9yl"T++KAPl6OV౯BF:6x'UHzN"RуZ__c=^ X!1$C[HPA6F:% =b]\J<{wsoxw3W8 Xy$ ٻ Rvs[W;NU޿U F@|)|gKLmmY#<6O`m`r*Mݴv@^d03*= 4(|- =Qjz᭭3N=nEqHXzPaU DKYV޿]27;z{SDEgd/{J Oأ_Im5/ PndXt\L;As]T6u^lJ`Wg5ᇴq ImeUKåZbK[[cqh}^ůukoS:Sf[qA3ITE%D=ƯKs^_WSSN~ewN"ll.ȅmn~gfa!2œ| XR+tW !@lT~ <1# r'뒍% Kx0bJ ؆8NQXD6F* no ʋV8ax GW#/:ipjM<"?=Wz~9naol o[1.~' HuS촿رG0+Crs4=FƋڽ}}(XGZL6S~c6K?XڐM;7s#bv0怠oj2x&n)7mˉ/oV5,#yksC|~0GC6dQa%y(nYͱ8};R>@=l}0iMǷpiy,JXeYc/=9JH`=} F!VGs88<1߷xN!5XFzF%Uf mG*1Ge0)-8M-E8=Z8ll'NPI pE'V,ӊN`^7?(;F\<NZnbr-#N$c bFh~Vbs5wضVh x_5Gs*㲨 5.ոpkS sjTMqf<@\-^1'?!ai-y{j,0CBו/{ j&@Q]Jxq=VXe?b4;cbc̎cF[Mf9adPVx^=~;ȻH=!5{EhEy"h˷;r=L^( uv#R KEϑrTyXsS|򓟔gB,8б1yK<,,%Jң::8ْGGqi=v_JpX1׏@x :4\/eXsZ8j9vS.`É#瀝J'WMH{,"8^H͘ qѨ${r "Eeh¦4V$"•E9n9zg_=sb"؟7mefvV|w1 ZX& mQ (mjk_3]4{`. ˘&x~,.AAP$/R܅ XZlj3M;rȯa4ȫK7zj\t9tNHʒd"HtoƅGQ^xe'l5t"C Ҩc2~fxS˗/SO?-]NH M "^'ndSХ185qm9"t:?B!iko_?597vySՏU^+v\ih^Ӎ.*\NhR ZY_·W[h,!pz2i"KXR*p 6!R^aJPW#@>ru]f JSSXD˸Oa(;밳}cMO4mGW+c9P16o/py (@^]Nxd4FзvJ/5vp|v-H890ثԔÛg萵V2m 94;)h+}C mH<7n|lc3gq&<@l! әR).!2 kt<i}T*B0Rxm9ߝm%6*1J(яC}GGG .h‹\^#z}q@/]jGԪ}wlG m IFhsrrь!d @Gq|wJBLA Vp;%n7!=x Y^^@ކ0!ϙq[֋4;9RaZ}ecUIa_|=[.\Ņu#iB5_YPꄀ8v|0H}p@ gt:#'N6YZZV$E埥?*!XVn4q]Y6u 5}?!Vq f׮Kp@o;IduiLEGV[>,λw:!3u)@=.yd{tjp^ TE ylX4*CCpp~KiOS[xikQSnkw?}|fY؀>x-gnwQ帤[:JhIpV]4*ʁ嵉w-kѸzy*ap gz1tWN-G벶*_'N@VÝkkgwh?c9^ W/U KKM۲qn#bX촨 fV #}@..Cݲ43= +|F6y\7Z{/vZnUL.P"c(MѠ,/-#X ↔Qn`5PdG?Ź M,bS755{7:pca9͛iR[?Lhw~>=?iT7wo&c6Ñ6jf o0KK̉[7o_[mܛn' rPVq9gF@ jѡd1mASy(R;p%ekå$:%?S>L(Al*$d UO4`k{m\ڐy219!% J/?JY5N gdhhHV́ZwZ]cK!6&4x.$kd#7۰\Y]1iQmNmQ]?vU߀`B8<}t壥n{aa^yNMMI7m!"KYڢ\wW2 Juθ`/R'@79pµJg?|9Z<䵖N.Ӆx6å@e_'4 Vt*}shF`Lr|Ġsܜ TrF ;4-R7|qe@&x"<֪N8.bJѢus]F:A捆 ]fF .)ڳijwcfA^}NE,7JG>~ܸ[ј.*q•l6xk sC훑p5弈izgg)ccwdeeU>P$ R;ԸQ;-}@58/%s?k}ݡCZbNfa2L:`?yҀ q&R=V X [PaZ#(5ǽيcU[6/o"}xOw]pBMˈLiie7J(qݷ-ZZZeppHImlϵ́b`Y?SY4/b]I瓩$cW8~`f\?{.èҀҞ!佷]Tls@+}tmyǑGDzx˵פKz{z_UU=hANh4*'N u?.NpA;h|́F@>+W SLqa0D*lݮ[tʋ43/oFbjEKlB mU ?QCեz(T~^{D>۴m8?A[Kͭȹt*yS!GI8awEe\a2n.N~_@( Bl XbX -́R9ss;gll6"kkP6nޔnݹ=PJQe״~&N N&dާXQAT-*\`Gה6 [`s3%hMw6aQ|ٳ.DW9G 8I"ԉZX\-ǓnE!BL> ˯\򬭭޾:jPA>9@Uߝ~=WXS$ ŋrw뛱hlNgY]ƚN| -}>$N]J?g[q`2t&To7^W~s13VF OۂAU /"ra![h7iEh/b9a@9P.,Oc* R:%VbIґGpO|?zsq|69>z\9o T?0Rߚ*YYz޶SlvaMV>h.ϠSݼq -oR z 6xSSҁ| *o-8yBK.b&a9=>'f '2C#nfNo~gEԇyi?Vsj^e&໷%d̾eTvQ))9@8mk+˫ XlOtӵR&zq  y3[<$ńgb…Y)qX\\gϩզMy󖺨է]s8>)'=911!L w!KKrԴcX?~Gӗ/_ G A^ NJ+A 8:&0tO;@J 䙕ǯ)43jj@IDATdz۶0Lg]pjPњ|߅|o!Pv]8rp!\hL| U[4[__WxN t8tPzRxM~?tv鐞@Wyk|h dz{~7Ni?׾YFV5 ) >+%G[@;VF {zԅ֗ǁLOw}%=#fVNn߾-^L70G K^Q%lQ2g.=jM_^^0Ծ6#ÉC\!TV yመIfuuUR鴽zƗl+ )yhw &ΆH3\|Ly @,[2 _3gg^yEP4B$b18!W b n8A^Lձ#ԱȵP `Kp<7L+꫟~'U-Ҳ,!4B1Hg⧗O1?Oq uե=^@' on ϝ;ui¨>iQ,$>+^ťsմ>x"/n:?]8=3T39zYA>0tK&76֑b^9-Bd<40_D Wϱ~j鮫ʪU՝6#+kxppO tmfgd9G.?!GGUN KR҅Xy4&4Q|TI=XxV倣Pp8b^T~8 ib,<ޫ|Ald-P@NoIHcxqL':ӵWͭ^-Fi'+Ql+%IN)- ]% MUrPyjE;:]y4aGuܜ I!EQ:`^sd>+:|'gz=є֋*Z0R "KT:۶qk/_ιߖִ>8%y 6 4Fdgj*N4d< O}^udR0}%Y+Fm)9+_zA:pA܄yDf Q`ɎF EE rRqwss;-aht ޴ryLNNL~]GXғGI9=~49@ +=?;2ZۚM1/Bd0-+cq7ԸqtlI܈~⬳K嵥d/GGv!CP[1M;1!|s-Q o~iB1==-+0euܛׄ1Y ɝuy߽ݱ-Ccs@ƞFӛuޓ"ۊd^@shxзb г(h!Ъ .1<(+V4VhѾ"Ko_fTWzN4TJ}|PڏPH;Sr/5ق}(o͕{7p/Fݬ-7~054;_õ2au1%2k[)"^s0bl5ՠ37ӧ#&1 FE%zU`kׯy ߆O֖68T !%19@~w #_n\tPo5|W\p816~'N12cSSS.*Өm`9D :ρٸskkPx )fgC̭{-i& 䩧cXPB211 Ywn_\A0U0 WkZMpȩs{GVFbp_>#-"tF¡^t޳~5mY{ C-@s8jz^&_4SMMMW矞1T& :@ 21%*I[%Oar Â??ɢ,HZ at+I#90TrF(܏(?!۲r:ұ_lݛwy GJ&:Aۀo?xJ$fC+UU|H앥),_N%pxs0=? AʇJh laQ"_XK.ϧ $O?#ssb @:]'/ǯ#Hw}eA$8b)N=Pt 8%yw!^C{XƱ/Y.ߺ5xsA^h㎇ OFF[ pH_0]3rٖ}vAE#Z]]Օ8J!QkUҵ\VDbZ*< /0ܹ#+8,_?$?Я 2'yZW*+{Nwk-v-w#!haX\ A`1'^~<9|o# 4;&]ä1U-u>'޶P.x0⊇7Bo#JYKgU}pĈ#Iz2PR5B9-!U=_%9u?!+qB.dsRэ;-5:'v_l*[DBvt꾤v:+ H(@&T̊aBekaQ&#Hĺ08;_Q"۩rֈ6kGgp']_W:dF7<^́8AC_jQaK9okia069tx2<GHV$ߺu^Z\++K\vىHַՙQjs@zC?b^]HlKKy7{q3T:vg3Y A؋/+ҏ!QY^8KLB_8B,d iu; ;E>䥒iu >r&c۝*V,rwfOZg; %{;N9rWaX1W~sPQ^/LRťV4nv~ v( qEUPeKj;~(bmNۂݎkZb=knmESl[`E ĵd)Q"JqoU0L cb*p'b*II6֥ÆAWz&;kxӞu/9rǥh?Vz?Exm;~N8>ڨTs`q@/ 7 ?FJ$k p,$' kQa&[-v<AMsssS7⢌<`qhaښTz40.{I;Ҭ!> T*NX"@QdŰ얶J3r8cxKp? >y9<_[~x#[}O}sxS$%Qv,[dq$Mdf fd#19A+%Y/>f8L}wWW>!5p83TSu4{]<{p fQFyV|ŊIؐ2.Adz~wZak~l6Sޗ?9̪k2x0RG?:i)d:7OI{煭HΓ:y`pvd2>oo\d"=) .#!B]ĉG6LFl-/زyҢL{hh)LZe'nJDPPeʸH8aL/bt4"N2 -G~-ƺw/պػOGk_LWK峷~L~ܽ׼d/.. a;1aCEqZS΅Uuv~)v=+X$>$w_<WX" XͬJÎ@cGntxSWTC u ˡ^Ɉ2ohWVjHȡ < ,٧O.~_7DLP~Ÿ))~{x{FomuKOm-o ^h{x7{cm ӷhQUssbv%@>ah}ů?;fP[q1vܕo ʁKZ+>Zz-ׯp ba[2F_x +WًvkX,!*j%<84bx{ئb̋'V=9m>1!F6uZiݢG~|v2_~w'[gAoKۊA9<#-Q1S+JYQyJ[bK<]GIۊ A n"/A`'( r] ;IԬ =g?D\GMT: 7lES.^22XMӻ +`4K]Q ,%Thت1h7u` db F䭉oIO ,-$S6?DiݚoM[봦woZ۴>p\㜗8.+b_r Vp#A_r/jɓ͛8O(vm/<d'q^&U۳J@tŒ("g9Kq&+O`L+" 3g 0~iQ:{ FE+ǾJȸMSb EI5-7\l#Ϳu.M[{e`D,-1kn9f6kVܳsO(I-w?V*=}lZ+Q1+}*k򥯩jbR]LU/d>][̲G96,yu;jKPE=;ҷuֈ-ǧQT E&ggu*Aqz>l EI{2]. pJ[1c ѧ2E󦭚o!O+tߐX%,p.F+d?xM.ڇInc{o+ԕ߀mfZJmuD::G1>JHK WmY>e?Y;xy;M@0$>YfeHF,C@kX]퇋VDiR122 wua#Ec[_E0y{U(z##J:]Hn\7`\*2`,|Rf B(bRiȴ!5g d?ݲvyko`MZ>z圲~ffM>A(B|B'ܺk-޸yx^~ձ6ݷ6BS(vM)>4~m(IzldծO7f3!i'Ia$ZLO nK]v ە͢qtwuY+ H0nJHF풀!I zcpJ ,=x-u%F5EɗbKܶ\ zQԺ2,|>ҙgF\FR)ՠSKjkԵߧpW-8>ڀUv,):Q,z !nΞY-,h!CN ~=?JXS Z=3jMZ#2Wfx!c_}]}*5X&Olœ)"}v%# v1bg}[b,}r;uhG= -Dd>ߕvOܖR؄ŊSo\)2VQ̡㙐SFYg+ؐ H>(ox-O:bpf$1#,̟dr7W6&ڶCCjq]dfqNLL d.i.hq~[2,hÁj!X3?IEO(5"Y8,_yCl[w:;jv3FnD$Lh>=9)=OJG~ sq)qr+} tyFܢץ닃JT]._oXhܜˣ$ OSIv{sV&^obozFX{갎l?zx֭QzM/(rÓ IOoKȸMy)"SNx2F|rBʧb(l,lѮccc-zˈF9S#dzl:@޴ZmStP[!q nЭVjiv,׻YVy9D?oU5$mQ!K{]C@J<&`l[*1{wT*߬Uʉ[G7hm?,/Ęg?8*hov XP9^UEԑòݿo\W3 6,w~dTY2 s"˷d>NTM'G ^K;{bޔ2]M+-xj3(X*U٫%-TUtJR3`l نG\H%Ntx;x·"J\g'/D+"//ν+⋅\,(f|~ -eˈNL<3\6@L^ǐUCCæ1b-ook xxo\[ssM _9 {s(k\bL]j5]VgK5D!1s|B ay~\bwI@'1Hιg070{3^KgԸ[F!XP~7xqwnvvZ=44TDm1$HV_ Z3hnsNjQ3O6'|'7UP<ۚ̈tkgU]zl(H>y#(ISV^0:i4F{ VoQNa7l+xo-akۻljk| QyMοjVGT&z^CTTT: kT(-$VW2B9Y?_$oۍx\*W9?u# )Kk4bbKn@Ld{o8D&2'G~zR Mg>uusH@ElW^W%yZgQ,8ntx+|;1+{=0yݘn7.$. Zb.A_8}o+hDzr^FhJ!53i}Y3Iډ혉eTxѼg +uOhT2Uux!~|qKf!:hoC6;E^ڮ! ٶ"z0 jdlq~Hׂ?ۨ<Ȅ7nWs\~60 fﯢ82={Ԟqܫӧ?4mhCT("CWZa+EUX"rcy"=[E,}knNeE孟gݏ J~ WU9 #~^ug3͢w^:tz饗>;tay4Qf9WٟD;F%WB+Q{] bJ*"؉bʯ~f*T2J'3|)yJ m;NTʕP(PZXG /Zu6hRehv ]12vޓejfE>"k4[ZO|ftm8i^9]Qbd,7p9r*.l%2s25+ˢeJWV?qLTH(*5TjIY jC~WRXn\+|AI{X\uW=q11rDصFR**F{p6,BvnUϫQH^F|w%#0("ĚRer^Pb+C5_i՚;/p9y4׿ {sX]yR4A\޺E{3Q;n%I]7N,MD[ʔQٳ<]U?udN~ri'aR vҐ[,>)&v+XRuWWאV P#/jTL&&Z۰E-Xdɕݟ ZSGiW7\RVŚV#^mK W#,H̷;$n $@&(QʸZI3pU-p(O@<(ʙܴ66/bxeesQ柣'mjxhH͙ < `3~ cAg$mռ -&VLrh dHKt}u7P_SpFHwqu1g~u;؉.|C驷6<{^dЪӓR޴`\u],~eNS1$E"-L\ S3Ʋ'LEu.'`8V]A }7' CXl 3}InتX*Wʢ=y+d"!/'h7K6j2}w%xf)*-{)+[bǸ}8첲9D7qM4V TT90M|ïqO{q ,'ծ^5P _@[(ĕ:,FqJqҲyn6_6{t襻/d: ׳TXT==*ɠ|I-?xòBְkD .T- Rd[0vǔ:x֍7ug%c!g*O,Z 0&ws{vqq]3L^:R軫._ ,eāzj']mW*Pd4bTŎ>jCSlkPix|z,aH*c}'vo;k{bh"l\ԮW"$vOgH, +6[KИ-ą[J,1S#fSZm2n9$bUTեKπZUl%nI֐>鏻S5SBqƔSz}ݳKwyD^OhDWemG]:idҾl"*\j]]_$(!w@w.j>qJdrF`A3N(o[wb}J {˅\)M']qCLBȲ!XF!Whf,{X *[2ŖeJ3 dZhF'~JϽwUgko)\dk:pkӰv=9TbٔrO'@錸 <,IԐ_b_e^59;PGmF냛FnX]1SdӪ*ܻpWn똴Hl7sH*| u(]@OA4"*Ե>M+(*rÝl8c>l(2R˟ϵHx/2r#xlߣ(=kSNvx1~) |#zȴm?T>ӽ23;rjC`{' |ãUq':zItlsCYBߜ Ft˧+^uEКvWu]+Sɽ 4!>!Z!>% #Nth6B-BFvJg?ժV;c87o^5}kܲűV$u`_KW:"@vQG;:;M]a;`C˂ڴ0CcTHQ[琇۞žk_]|΢\|hl.O6F)rcZɿ_qypqL*O&WF}b U(¾X|rb%EG_z|.MO]Ѩ5-j"Fj2f3;jz")ʱкwO7JavѩJwF|w"p%,J]HS\UBL^{="K9͹yV[_\苕ؾ\qKnDIFp8QR\bC{&FP-Wk졣]o_=} ~7 F 5)"zJ9u!h_X{UW̋u]XU=+/yYi+!#6QDWm{ f" .}i]ϡbܲP,퓳H@O"or$] ȿw{}!(Siv/!(/ļ!dIi:$vP%$ bgpdx`@ 0 "o)>tdc?d 6 FGFwˑC(r3cIPw+Q>SPf~7΃+7 A> ]Uږr7om39:7FRt#JāſEW\m%k^zM{4~(G C@G$!4"[=㣣pYhglϣQ8]Lw<ʗC\Bw7oջSNR^;w㘚W}*^S$~Z|pCP+ߎ[̩ 2lF (ZDI'ܜrSPa)>dn'S$",xʈV3i+0]:rk>/RXH(qݎ!a %7V|oVzGG 2g}hwמ(,VZžwؿ/ؐ#gO*咉2zgAtH72fVeSvR8RMp@rTƆql::yQgq[@[C[ÙG!ؘ<;eГeA~JڂLJQ:j8|Rʗ*X"182x':twFL. #pٸ_AY#jrj/>i&qe+p8d,fdF]PP)+􉰃h5 "&b,7xoBZnuC >nix3,z"D,wRX- iD Jm XkV]6YO /91;r -#mم@$@G@,}_r1?^1{=C| H(X0o=uÁ@0`V_}}sK:\.+{B-,Ϋ` Un\iE+6 °h 5G"@.2H}-~k;ۮ%͛3Ӱ0\p)S1yKQ\n,bR]*ihF3]% f`XߠZOv'Uz")ͼ:X8шS`j%/Q$P8uW|YqXQ9E4wiN82< 4I(?(]+EL+5^n |k5(K/|'KKWVVv\ Pnرc2J&Ǒk2wQm,5t &;IDAT]"`4.0j 9UDܠ}+ޕkWԱǍ6\ )L͏]5jPZ=3W! JQhQJ$U q'Bh2Ib,Iv fUNEbjQ5oFy:>PC 9a*KlL"1I- k@1(wN~ `jFB9zaTkmD"Ͽ◿#߸sx| )Kkse$lHo_|Q(?FuCJ>t$r@$ kX\1ahi salz2%QDܴ/6=5 a2 k[ 5STQ wLcM A"sA WZa9XTNSPº蠾`.Y5U-*ػ80rWP; +{QB7jٳeeҙ\.K-tWtR:o%W}ze[p )X6x֭Io^JEXj* wNE7\Z^jd5rmD~Vw/չatXqC\({~k+("pK \}eNua[m}7QpKdW >t" }ic?ݔ< سhJXp b"d(Xe `%vP=nuQ$hC_Bm5 }{%L\eK/cx$,wվ }v{[R9408h>pܱYdPKKKpU e\:+++_YqQLK7s lJЇ~_A;ٽꎫP.DmQ\djԵ[}P^RPX&=҉"En 'ڭ#od[vwع6^*m[tY諁ua[DF  ~V8 \yWW*oaj<NHx/'{<: (-b9(طYM!ID'G99(2Ği!ңlЈ#caj˂NR}gpgZ]v:jYͮGN!Yt2bn-5'٨վعA$b'E)|ua+j=a+<84`kX {'qo_-mEJj!Pw!u }߂%s, ,d& ?KWqels\4\J7% l/y~lHH`xCNDA$Y?/gJ5t/E$4 1# %=Ґb01 $y0ze(Rbx0e!Bv:!icsaJPx*Ro4a *ءTރ&U3mhWcq ņGq q97z/A὿r_Bb ՘w=,h.|Nwc] @'6HHH#Pmmv $@$@$@$(8G$@$@MVξz   ]H@E.$   &@WO$@$@$ P›K"  lx}y$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$?{ĈDIENDB`smol-2.0.1/examples/000077500000000000000000000000001465701726600143205ustar00rootroot00000000000000smol-2.0.1/examples/async-h1-client.rs000066400000000000000000000036551465701726600175760ustar00rootroot00000000000000//! An HTTP+TLS client based on `async-h1` and `async-native-tls`. //! //! Run with: //! //! ``` //! cargo run --example async-h1-client //! ``` use std::net::{TcpStream, ToSocketAddrs}; use anyhow::{bail, Context as _, Error, Result}; use http_types::{Method, Request, Response}; use smol::{prelude::*, Async}; use url::Url; /// Sends a request and fetches the response. async fn fetch(req: Request) -> Result { // Figure out the host and the port. let host = req.url().host().context("cannot parse host")?.to_string(); let port = req .url() .port_or_known_default() .context("cannot guess port")?; // Connect to the host. let socket_addr = { let host = host.clone(); smol::unblock(move || (host.as_str(), port).to_socket_addrs()) .await? .next() .context("cannot resolve address")? }; let stream = Async::::connect(socket_addr).await?; // Send the request and wait for the response. let resp = match req.url().scheme() { "http" => async_h1::connect(stream, req).await.map_err(Error::msg)?, "https" => { // In case of HTTPS, establish a secure TLS connection first. let stream = async_native_tls::connect(&host, stream).await?; async_h1::connect(stream, req).await.map_err(Error::msg)? } scheme => bail!("unsupported scheme: {}", scheme), }; Ok(resp) } fn main() -> Result<()> { smol::block_on(async { // Create a request. let addr = "https://www.rust-lang.org"; let req = Request::new(Method::Get, Url::parse(addr)?); // Fetch the response. let mut resp = fetch(req).await?; println!("{:#?}", resp); // Read the message body. let mut body = Vec::new(); resp.read_to_end(&mut body).await?; println!("{}", String::from_utf8_lossy(&body)); Ok(()) }) } smol-2.0.1/examples/async-h1-server.rs000066400000000000000000000063121465701726600176170ustar00rootroot00000000000000//! An HTTP+TLS server based on `async-h1` and `async-native-tls`. //! //! Run with: //! //! ``` //! cargo run --example async-h1-server //! ``` //! //! Open in the browser any of these addresses: //! //! - http://localhost:8000/ //! - https://localhost:8001/ (accept the security prompt in the browser) //! //! Refer to `README.md` to see how to the TLS certificate was generated. use std::net::TcpListener; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor}; use http_types::{Request, Response, StatusCode}; use smol::{future, Async}; /// Serves a request and returns a response. async fn serve(req: Request) -> http_types::Result { println!("Serving {}", req.url()); let mut res = Response::new(StatusCode::Ok); res.insert_header("Content-Type", "text/plain"); res.set_body("Hello from async-h1!"); Ok(res) } /// Listens for incoming connections and serves them. async fn listen(listener: Async, tls: Option) -> Result<()> { // Format the full host address. let host = match &tls { None => format!("http://{}", listener.get_ref().local_addr()?), Some(_) => format!("https://{}", listener.get_ref().local_addr()?), }; println!("Listening on {}", host); loop { // Accept the next connection. let (stream, _) = listener.accept().await?; // Spawn a background task serving this connection. let task = match &tls { None => { let stream = async_dup::Arc::new(stream); smol::spawn(async move { if let Err(err) = async_h1::accept(stream, serve).await { println!("Connection error: {:#?}", err); } }) } Some(tls) => { // In case of HTTPS, establish a secure TLS connection first. match tls.accept(stream).await { Ok(stream) => { let stream = async_dup::Arc::new(async_dup::Mutex::new(stream)); smol::spawn(async move { if let Err(err) = async_h1::accept(stream, serve).await { println!("Connection error: {:#?}", err); } }) } Err(err) => { println!("Failed to establish secure TLS connection: {:#?}", err); continue; } } } }; // Detach the task to let it run in the background. task.detach(); } } fn main() -> Result<()> { // Initialize TLS with the local certificate, private key, and password. let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?; let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start HTTP and HTTPS servers. smol::block_on(async { let http = listen(Async::::bind(([127, 0, 0, 1], 8000))?, None); let https = listen( Async::::bind(([127, 0, 0, 1], 8001))?, Some(tls), ); future::try_zip(http, https).await?; Ok(()) }) } smol-2.0.1/examples/certificate.pem000066400000000000000000000022641465701726600173110ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDSzCCAjOgAwIBAgIIHBgnI8QV+zIwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE AxMVbWluaWNhIHJvb3QgY2EgMWMxODI3MCAXDTIwMDMyNjE3Mjk1M1oYDzIxMjAw MzI2MTcyOTUzWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAxYzE4MjcwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5CGrfOjgWhDC67fUL5S/GSYC4 8jP+/NLsxY8bbmohQv9TBOl3Pxv3w5uVXHBhpwigaOyRqvF3+U/YoZgT99QkV+5c XjYjdxgmTEl35eZfT7kCr6PjxLuVOIYdMEYVe+7JgkIMZjEMkRq3giBJxIQ86FWi KhCyg+5vMZ0ZYbfiv+yyfS41JebgJP1WbuR/boT3X90EuyJ3U2F58lOZadXdqJ0T iTLXyZzy7XKFP+3/dhB3NO5kfHhuP6sCHc6ORfkbYUE+rxuwKgwtVdYoBEI9mZMo bZKkyUN4xOzupYKFpKXRBc0UiQKsl+RQ6VCrOM4sc4rWoWHLvj9esP4rbhu7AgMB AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTEJVhl2F/WAKdO Br26/llSB4kYSDAfBgNVHSMEGDAWgBTEJVhl2F/WAKdOBr26/llSB4kYSDANBgkq hkiG9w0BAQsFAAOCAQEAkvxlm+mhMn/UBV06jxTLNFan2ttQ79ms7T7EkbQWW55f R3ytzumfR52np4u04K0L6xHTE5vLuNiebq5s8IU7GiWcJWNkCxcJejzgAukFgsbb Ffqg8wBS/tbCaw32btk/lFrJ74t8Q13QDQVSRO9S8S4bHtiSluyV65oPKE2hXbiS euISC+9Yf2Eb4xCqIIb2/hCUq6DF9/tkAffapjbrIKQQjrMqvX6734X7g2N7cwvD Lt7U9YI0CuWHu/L+cpo7+YrBMRgM8tECip61XMQ6oBgJt3ZNMnrYiMkwCeWstKFD tDQJZyAJHE4wbInYqi78rQRMo1eoAfdFcgdS0fyDrA== -----END CERTIFICATE----- smol-2.0.1/examples/chat-client.rs000066400000000000000000000024671465701726600170720ustar00rootroot00000000000000//! A TCP chat client. //! //! First start a server: //! //! ``` //! cargo run --example chat-server //! ``` //! //! Then start clients: //! //! ``` //! cargo run --example chat-client //! ``` use std::net::TcpStream; use smol::{future, io, Async, Unblock}; fn main() -> io::Result<()> { smol::block_on(async { // Connect to the server and create async stdin and stdout. let stream = Async::::connect(([127, 0, 0, 1], 6000)).await?; let stdin = Unblock::new(std::io::stdin()); let mut stdout = Unblock::new(std::io::stdout()); // Intro messages. println!("Connected to {}", stream.get_ref().peer_addr()?); println!("My nickname: {}", stream.get_ref().local_addr()?); println!("Type a message and hit enter!\n"); let reader = &stream; let mut writer = &stream; // Wait until the standard input is closed or the connection is closed. future::race( async { let res = io::copy(stdin, &mut writer).await; println!("Quit!"); res }, async { let res = io::copy(reader, &mut stdout).await; println!("Server disconnected!"); res }, ) .await?; Ok(()) }) } smol-2.0.1/examples/chat-server.rs000066400000000000000000000065611465701726600171210ustar00rootroot00000000000000//! A TCP chat server. //! //! First start a server: //! //! ``` //! cargo run --example chat-server //! ``` //! //! Then start clients: //! //! ``` //! cargo run --example chat-client //! ``` use std::collections::HashMap; use std::net::{SocketAddr, TcpListener, TcpStream}; use async_channel::{bounded, Receiver, Sender}; use async_dup::Arc; use smol::{io, prelude::*, Async}; /// An event on the chat server. enum Event { /// A client has joined. Join(SocketAddr, Arc>), /// A client has left. Leave(SocketAddr), /// A client sent a message. Message(SocketAddr, String), } /// Dispatches events to clients. async fn dispatch(receiver: Receiver) -> io::Result<()> { // Currently active clients. let mut map = HashMap::>>::new(); // Receive incoming events. while let Ok(event) = receiver.recv().await { // Process the event and format a message to send to clients. let output = match event { Event::Join(addr, stream) => { map.insert(addr, stream); format!("{} has joined\n", addr) } Event::Leave(addr) => { map.remove(&addr); format!("{} has left\n", addr) } Event::Message(addr, msg) => format!("{} says: {}\n", addr, msg), }; // Display the event in the server process. print!("{}", output); // Send the event to all active clients. for stream in map.values_mut() { // Ignore errors because the client might disconnect at any point. stream.write_all(output.as_bytes()).await.ok(); } } Ok(()) } /// Reads messages from the client and forwards them to the dispatcher task. async fn read_messages(sender: Sender, client: Arc>) -> io::Result<()> { let addr = client.get_ref().peer_addr()?; let mut lines = io::BufReader::new(client).lines(); while let Some(line) = lines.next().await { let line = line?; sender.send(Event::Message(addr, line)).await.ok(); } Ok(()) } fn main() -> io::Result<()> { smol::block_on(async { // Create a listener for incoming client connections. let listener = Async::::bind(([127, 0, 0, 1], 6000))?; // Intro messages. println!("Listening on {}", listener.get_ref().local_addr()?); println!("Start a chat client now!\n"); // Spawn a background task that dispatches events to clients. let (sender, receiver) = bounded(100); smol::spawn(dispatch(receiver)).detach(); loop { // Accept the next connection. let (stream, addr) = listener.accept().await?; let client = Arc::new(stream); let sender = sender.clone(); // Spawn a background task reading messages from the client. smol::spawn(async move { // Client starts with a `Join` event. sender.send(Event::Join(addr, client.clone())).await.ok(); // Read messages from the client and ignore I/O errors when the client quits. read_messages(sender.clone(), client).await.ok(); // Client ends with a `Leave` event. sender.send(Event::Leave(addr)).await.ok(); }) .detach(); } }) } smol-2.0.1/examples/ctrl-c.rs000066400000000000000000000010701465701726600160500ustar00rootroot00000000000000//! Uses the `ctrlc` crate to catch the Ctrl-C signal. //! //! Run with: //! //! ``` //! cargo run --example ctrl-c //! ``` fn main() { // Set a handler that sends a message through a channel. let (s, ctrl_c) = async_channel::bounded(100); let handle = move || { s.try_send(()).ok(); }; ctrlc::set_handler(handle).unwrap(); smol::block_on(async { println!("Waiting for Ctrl-C..."); // Receive a message that indicates the Ctrl-C signal occurred. ctrl_c.recv().await.ok(); println!("Done!"); }) } smol-2.0.1/examples/get-request.rs000066400000000000000000000016261465701726600171400ustar00rootroot00000000000000//! Connect to an HTTP website, make a GET request, and pipe the response to the standard output. //! //! Run with: //! //! ``` //! cargo run --example get-request //! ``` use smol::{io, prelude::*, Async, Unblock}; use std::net::{TcpStream, ToSocketAddrs}; fn main() -> io::Result<()> { smol::block_on(async { // Connect to http://example.com let mut addrs = smol::unblock(move || ("example.com", 80).to_socket_addrs()).await?; let addr = addrs.next().unwrap(); let mut stream = Async::::connect(addr).await?; // Send an HTTP GET request. let req = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"; stream.write_all(req).await?; // Read the response and pipe it to the standard output. let mut stdout = Unblock::new(std::io::stdout()); io::copy(&stream, &mut stdout).await?; Ok(()) }) } smol-2.0.1/examples/hyper-client.rs000066400000000000000000000103161465701726600172720ustar00rootroot00000000000000//! An HTTP+TLS client based on `hyper` and `async-native-tls`. //! //! Run with: //! //! ``` //! cargo run --example hyper-client //! ``` use std::convert::TryInto; use std::pin::Pin; use std::task::{Context, Poll}; use anyhow::{bail, Context as _, Result}; use async_native_tls::TlsStream; use http_body_util::{BodyStream, Empty}; use hyper::body::Incoming; use hyper::{Request, Response}; use macro_rules_attribute::apply; use smol::{io, net::TcpStream, prelude::*, Executor}; use smol_hyper::rt::FuturesIo; use smol_macros::main; /// Sends a request and fetches the response. async fn fetch( ex: &Executor<'static>, req: Request>, ) -> Result> { // Connect to the HTTP server. let io = { let host = req.uri().host().context("cannot parse host")?; match req.uri().scheme_str() { Some("http") => { let stream = { let port = req.uri().port_u16().unwrap_or(80); TcpStream::connect((host, port)).await? }; SmolStream::Plain(stream) } Some("https") => { // In case of HTTPS, establish a secure TLS connection first. let stream = { let port = req.uri().port_u16().unwrap_or(443); TcpStream::connect((host, port)).await? }; let stream = async_native_tls::connect(host, stream).await?; SmolStream::Tls(stream) } scheme => bail!("unsupported scheme: {:?}", scheme), } }; // Spawn the HTTP/1 connection. let (mut sender, conn) = hyper::client::conn::http1::handshake(FuturesIo::new(io)).await?; ex.spawn(async move { if let Err(e) = conn.await { println!("Connection failed: {:?}", e); } }) .detach(); // Get the result let result = sender.send_request(req).await?; Ok(result) } #[apply(main!)] async fn main(ex: &Executor<'static>) -> Result<()> { // Create a request. let url: hyper::Uri = "https://www.rust-lang.org".try_into()?; let req = Request::builder() .header( hyper::header::HOST, url.authority().unwrap().clone().as_str(), ) .uri(url) .body(Empty::new())?; // Fetch the response. let resp = fetch(ex, req).await?; println!("{:#?}", resp); // Read the message body. let body: Vec = BodyStream::new(resp.into_body()) .try_fold(Vec::new(), |mut body, chunk| { if let Some(chunk) = chunk.data_ref() { body.extend_from_slice(chunk); } Ok(body) }) .await?; println!("{}", String::from_utf8_lossy(&body)); Ok(()) } /// A TCP or TCP+TLS connection. enum SmolStream { /// A plain TCP connection. Plain(TcpStream), /// A TCP connection secured by TLS. Tls(TlsStream), } impl AsyncRead for SmolStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { match &mut *self { SmolStream::Plain(stream) => Pin::new(stream).poll_read(cx, buf), SmolStream::Tls(stream) => Pin::new(stream).poll_read(cx, buf), } } } impl AsyncWrite for SmolStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { match &mut *self { SmolStream::Plain(stream) => Pin::new(stream).poll_write(cx, buf), SmolStream::Tls(stream) => Pin::new(stream).poll_write(cx, buf), } } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { SmolStream::Plain(stream) => Pin::new(stream).poll_close(cx), SmolStream::Tls(stream) => Pin::new(stream).poll_close(cx), } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { SmolStream::Plain(stream) => Pin::new(stream).poll_flush(cx), SmolStream::Tls(stream) => Pin::new(stream).poll_flush(cx), } } } smol-2.0.1/examples/hyper-server.rs000066400000000000000000000107321465701726600173240ustar00rootroot00000000000000//! An HTTP+TLS server based on `hyper` and `async-native-tls`. //! //! Run with: //! //! ``` //! cargo run --example hyper-server //! ``` //! //! Open in the browser any of these addresses: //! //! - http://localhost:8000/ //! - https://localhost:8001/ (accept the security prompt in the browser) //! //! Refer to `README.md` to see how to the TLS certificate was generated. use std::net::{TcpListener, TcpStream}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor, TlsStream}; use http_body_util::Full; use hyper::body::Incoming; use hyper::service::service_fn; use hyper::{Request, Response}; use macro_rules_attribute::apply; use smol::{future, io, prelude::*, Async, Executor}; use smol_hyper::rt::{FuturesIo, SmolTimer}; use smol_macros::main; /// Serves a request and returns a response. async fn serve(req: Request) -> Result>> { println!("Serving {}", req.uri()); Ok(Response::new(Full::new("Hello from hyper!".as_bytes()))) } /// Handle a new client. async fn handle_client(client: Async, tls: Option) -> Result<()> { // Wrap it in TLS if necessary. let client = match &tls { None => SmolStream::Plain(client), Some(tls) => { // In case of HTTPS, establish a secure TLS connection. SmolStream::Tls(tls.accept(client).await?) } }; // Build the server. hyper::server::conn::http1::Builder::new() .timer(SmolTimer::new()) .serve_connection(FuturesIo::new(client), service_fn(serve)) .await?; Ok(()) } /// Listens for incoming connections and serves them. async fn listen( ex: &Arc>, listener: Async, tls: Option, ) -> Result<()> { // Format the full host address. let host = &match tls { None => format!("http://{}", listener.get_ref().local_addr()?), Some(_) => format!("https://{}", listener.get_ref().local_addr()?), }; println!("Listening on {}", host); loop { // Wait for a new client. let (client, _) = listener.accept().await?; // Spawn a task to handle this connection. ex.spawn({ let tls = tls.clone(); async move { if let Err(e) = handle_client(client, tls).await { println!("Error while handling client: {}", e); } } }) .detach(); } } #[apply(main!)] async fn main(ex: &Arc>) -> Result<()> { // Initialize TLS with the local certificate, private key, and password. let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?; let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start HTTP and HTTPS servers. let http = listen( ex, Async::::bind(([127, 0, 0, 1], 8000))?, None, ); let https = listen( ex, Async::::bind(([127, 0, 0, 1], 8001))?, Some(tls), ); future::try_zip(http, https).await?; Ok(()) } /// A TCP or TCP+TLS connection. enum SmolStream { /// A plain TCP connection. Plain(Async), /// A TCP connection secured by TLS. Tls(TlsStream>), } impl AsyncRead for SmolStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_read(cx, buf), Self::Tls(s) => Pin::new(s).poll_read(cx, buf), } } } impl AsyncWrite for SmolStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_write(cx, buf), Self::Tls(s) => Pin::new(s).poll_write(cx, buf), } } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_close(cx), Self::Tls(s) => Pin::new(s).poll_close(cx), } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_close(cx), Self::Tls(s) => Pin::new(s).poll_close(cx), } } } smol-2.0.1/examples/identity.pfx000066400000000000000000000045651465701726600167020ustar00rootroot000000000000000 q0 7 *H  ( $0 0 *H 00 *H 0 *H  0Jpֽ~{y c:0K~ێ{I*~ M8ӳTZT)͵M}bBY+"CE?慚"-`$߳?swLBn1Ih㤵4X5\S0h| ~wW\ giU$[@Tw dPqq7KwQGBb*aRR&iU}ƅ0 ט_ե,]x2_">᯾ >3 QE}v̟a (ࣆ wH35) (5jqj1!5}n %a=UqS<A$?SӲ75j vc k rZW⯨nfkpcip@b8&ܧy^KIz(?r@uSI1:}(: N-.8IN+'SSk*`o*Z?fay\B.`#^wwRS0;+[kw>#M15WRe9Rډ-SڝwQe FKzΙ2up>uĆ秅t4T`cC`E.ϾfJjfp6Ȯ͚W;G 0A *H 2.0*0& *H  00 *H  03)YBj 4i!sFŽLDoA€r^F&V*x}npU 5R8ˠv.I)c8xQs(0J س\)NQ&vSc7bLKLq b>Q;hX7@Qm %Ln,ls0}fцܚAvԀpXcFXd$+T FԫL [ Kv>MѬɏ'7́`N<0rOp6B>劳r2͗Wy {9>0,5 FI%`G_I+՗1Z*XWШWWrd3CG5N6ZD2`,)N!pdr)0M(a<(@ĵy`cͣQaITӴ/J˩OzuHwg[5iGY>FM@ߴ-sPcajxMfΗ_e|d3nXrNL bv!(ε3P 62ܳY̸mzh1ؾ[mٞ-ۦ| |['+> #"V]!1d:=Zlt٘R'V(uWg&"/MsS-:)E!@@U66]j)^"CŁΠܩԀ8EWl std::io::Result<()> { use std::ffi::OsString; use std::os::unix::io::AsFd; use inotify::{EventMask, Inotify, WatchMask}; use smol::{io, Async}; type Event = (OsString, EventMask); /// Reads some events without blocking. /// /// If there are no events, an [`io::ErrorKind::WouldBlock`] error is returned. fn read_op(inotify: &mut Inotify) -> io::Result> { let mut buffer = [0; 1024]; let events = inotify .read_events(&mut buffer)? .filter_map(|ev| ev.name.map(|name| (name.to_owned(), ev.mask))) .collect::>(); if events.is_empty() { Err(io::ErrorKind::WouldBlock.into()) } else { Ok(events) } } smol::block_on(async { // Watch events in the current directory. let mut inotify = Inotify::init()?; let source = Async::new(inotify.as_fd().try_clone_to_owned()?)?; inotify.watches().add(".", WatchMask::ALL_EVENTS)?; println!("Watching for filesystem events in the current directory..."); println!("Try opening a file to trigger some events."); println!(); // Wait for events in a loop and print them on the screen. loop { for event in source.read_with(|_| read_op(&mut inotify)).await? { println!("{:?}", event); } } }) } #[cfg(not(target_os = "linux"))] fn main() { println!("This example works only on Linux!"); } smol-2.0.1/examples/linux-timerfd.rs000066400000000000000000000022101465701726600174500ustar00rootroot00000000000000//! Uses the `timerfd` crate to sleep using an OS timer. //! //! Run with: //! //! ``` //! cargo run --example linux-timerfd //! ``` #[cfg(target_os = "linux")] fn main() -> std::io::Result<()> { use std::time::{Duration, Instant}; use smol::{io, Async}; use timerfd::{SetTimeFlags, TimerFd, TimerState}; /// Sleeps using an OS timer. async fn sleep(dur: Duration) -> io::Result<()> { // Create an OS timer. let mut timer = TimerFd::new()?; timer.set_state(TimerState::Oneshot(dur), SetTimeFlags::Default); // When the OS timer fires, a 64-bit integer can be read from it. Async::new(timer)? .read_with(|t| rustix::io::read(t, &mut [0u8; 8]).map_err(io::Error::from)) .await?; Ok(()) } smol::block_on(async { let start = Instant::now(); println!("Sleeping..."); // Sleep for a second using an OS timer. sleep(Duration::from_secs(1)).await?; println!("Woke up after {:?}", start.elapsed()); Ok(()) }) } #[cfg(not(target_os = "linux"))] fn main() { println!("This example works only on Linux!"); } smol-2.0.1/examples/simple-client.rs000066400000000000000000000037631465701726600174440ustar00rootroot00000000000000//! A simple HTTP+TLS client based on `async-native-tls`. //! //! Run with: //! //! ``` //! cargo run --example simple-client //! ``` use std::net::{TcpStream, ToSocketAddrs}; use anyhow::{bail, Context as _, Result}; use smol::{prelude::*, Async}; use url::Url; /// Sends a GET request and fetches the response. async fn fetch(addr: &str) -> Result> { // Parse the URL. let url = Url::parse(addr)?; let host = url.host().context("cannot parse host")?.to_string(); let port = url.port_or_known_default().context("cannot guess port")?; let path = url.path().to_string(); let query = match url.query() { Some(q) => format!("?{}", q), None => String::new(), }; // Construct a request. let req = format!( "GET {}{} HTTP/1.1\r\nHost: {}\r\nAccept: */*\r\nConnection: close\r\n\r\n", path, query, host, ); // Connect to the host. let socket_addr = { let host = host.clone(); smol::unblock(move || (host.as_str(), port).to_socket_addrs()) .await? .next() .context("cannot resolve address")? }; let mut stream = Async::::connect(socket_addr).await?; // Send the request and wait for the response. let mut resp = Vec::new(); match url.scheme() { "http" => { stream.write_all(req.as_bytes()).await?; stream.read_to_end(&mut resp).await?; } "https" => { // In case of HTTPS, establish a secure TLS connection first. let mut stream = async_native_tls::connect(&host, stream).await?; stream.write_all(req.as_bytes()).await?; stream.read_to_end(&mut resp).await?; } scheme => bail!("unsupported scheme: {}", scheme), } Ok(resp) } fn main() -> Result<()> { smol::block_on(async { let addr = "https://www.rust-lang.org"; let resp = fetch(addr).await?; println!("{}", String::from_utf8_lossy(&resp)); Ok(()) }) } smol-2.0.1/examples/simple-server.rs000066400000000000000000000055431465701726600174720ustar00rootroot00000000000000//! A simple HTTP+TLS server based on `async-native-tls`. //! //! Run with: //! //! ``` //! cargo run --example simple-server //! ``` //! //! Open in the browser any of these addresses: //! //! - http://localhost:8000/ //! - https://localhost:8001/ (accept the security prompt in the browser) //! //! Refer to `README.md` to see how to the TLS certificate was generated. use std::net::{TcpListener, TcpStream}; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor}; use smol::{future, prelude::*, Async}; const RESPONSE: &[u8] = br#" HTTP/1.1 200 OK Content-Type: text/html Content-Length: 47 Hello! "#; /// Reads a request from the client and sends it a response. async fn serve(mut stream: Async, tls: Option) -> Result<()> { match tls { None => { println!("Serving http://{}", stream.get_ref().local_addr()?); stream.write_all(RESPONSE).await?; } Some(tls) => { println!("Serving https://{}", stream.get_ref().local_addr()?); // In case of HTTPS, establish a secure TLS connection first. match tls.accept(stream).await { Ok(mut stream) => { stream.write_all(RESPONSE).await?; stream.flush().await?; stream.close().await?; } Err(err) => println!("Failed to establish secure TLS connection: {:#?}", err), } } } Ok(()) } /// Listens for incoming connections and serves them. async fn listen(listener: Async, tls: Option) -> Result<()> { // Display the full host address. match &tls { None => println!("Listening on http://{}", listener.get_ref().local_addr()?), Some(_) => println!("Listening on https://{}", listener.get_ref().local_addr()?), } loop { // Accept the next connection. let (stream, _) = listener.accept().await?; let tls = tls.clone(); // Spawn a background task serving this connection. smol::spawn(async move { if let Err(err) = serve(stream, tls).await { println!("Connection error: {:#?}", err); } }) .detach(); } } fn main() -> Result<()> { // Initialize TLS with the local certificate, private key, and password. let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?; let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start HTTP and HTTPS servers. smol::block_on(async { let http = listen(Async::::bind(([127, 0, 0, 1], 8000))?, None); let https = listen( Async::::bind(([127, 0, 0, 1], 8001))?, Some(tls), ); future::try_zip(http, https).await?; Ok(()) }) } smol-2.0.1/examples/tcp-client.rs000066400000000000000000000017111465701726600167300ustar00rootroot00000000000000//! A TCP client. //! //! First start a server: //! //! ``` //! cargo run --example tcp-server //! ``` //! //! Then start a client: //! //! ``` //! cargo run --example tcp-client //! ``` use std::net::TcpStream; use smol::{future, io, Async, Unblock}; fn main() -> io::Result<()> { smol::block_on(async { // Create async stdin and stdout handles. let stdin = Unblock::new(std::io::stdin()); let mut stdout = Unblock::new(std::io::stdout()); // Connect to the server. let stream = Async::::connect(([127, 0, 0, 1], 7000)).await?; println!("Connected to {}", stream.get_ref().peer_addr()?); println!("Type a message and hit enter!\n"); // Pipe messages from stdin to the server and pipe messages from the server to stdout. future::try_zip( io::copy(stdin, &mut &stream), io::copy(&stream, &mut stdout), ) .await?; Ok(()) }) } smol-2.0.1/examples/tcp-server.rs000066400000000000000000000017701465701726600167650ustar00rootroot00000000000000//! A TCP server. //! //! First start a server: //! //! ``` //! cargo run --example tcp-server //! ``` //! //! Then start a client: //! //! ``` //! cargo run --example tcp-client //! ``` use std::net::{TcpListener, TcpStream}; use smol::{io, Async}; /// Echoes messages from the client back to it. async fn echo(stream: Async) -> io::Result<()> { io::copy(&stream, &mut &stream).await?; Ok(()) } fn main() -> io::Result<()> { smol::block_on(async { // Create a listener. let listener = Async::::bind(([127, 0, 0, 1], 7000))?; println!("Listening on {}", listener.get_ref().local_addr()?); println!("Now start a TCP client."); // Accept clients in a loop. loop { let (stream, peer_addr) = listener.accept().await?; println!("Accepted client: {}", peer_addr); // Spawn a task that echoes messages from the client back to it. smol::spawn(echo(stream)).detach(); } }) } smol-2.0.1/examples/tls-client.rs000066400000000000000000000026511465701726600167500ustar00rootroot00000000000000//! A TCP client secured by TLS based on `async-native-tls`. //! //! First start a server: //! //! ``` //! cargo run --example tls-server //! ``` //! //! Then start a client: //! //! ``` //! cargo run --example tls-client //! ``` use std::net::TcpStream; use anyhow::Result; use async_native_tls::{Certificate, TlsConnector}; use smol::{future, io, Async, Unblock}; fn main() -> Result<()> { // Initialize TLS with the local certificate. let mut builder = native_tls::TlsConnector::builder(); builder.add_root_certificate(Certificate::from_pem(include_bytes!("certificate.pem"))?); let tls = TlsConnector::from(builder); smol::block_on(async { // Create async stdin and stdout handles. let stdin = Unblock::new(std::io::stdin()); let mut stdout = Unblock::new(std::io::stdout()); // Connect to the server. let stream = Async::::connect(([127, 0, 0, 1], 7001)).await?; let stream = tls.connect("127.0.0.1", stream).await?; println!("Connected to {}", stream.get_ref().get_ref().peer_addr()?); println!("Type a message and hit enter!\n"); // Pipe messages from stdin to the server and pipe messages from the server to stdout. let stream = async_dup::Mutex::new(stream); future::try_zip( io::copy(stdin, &mut &stream), io::copy(&stream, &mut stdout), ) .await?; Ok(()) }) } smol-2.0.1/examples/tls-server.rs000066400000000000000000000030131465701726600167710ustar00rootroot00000000000000//! A TCP server secured by TLS based on `async-native-tls`. //! //! First start a server: //! //! ``` //! cargo run --example tls-server //! ``` //! //! Then start a client: //! //! ``` //! cargo run --example tls-client //! ``` use std::net::{TcpListener, TcpStream}; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor, TlsStream}; use smol::{io, Async}; /// Echoes messages from the client back to it. async fn echo(stream: TlsStream>) -> Result<()> { let stream = async_dup::Mutex::new(stream); io::copy(&stream, &mut &stream).await?; Ok(()) } fn main() -> Result<()> { // Initialize TLS with the local certificate, private key, and password. let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?; let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); smol::block_on(async { // Create a listener. let listener = Async::::bind(([127, 0, 0, 1], 7001))?; println!("Listening on {}", listener.get_ref().local_addr()?); println!("Now start a TLS client."); // Accept clients in a loop. loop { let (stream, _) = listener.accept().await?; let stream = tls.accept(stream).await?; println!( "Accepted client: {}", stream.get_ref().get_ref().peer_addr()? ); // Spawn a task that echoes messages from the client back to it. smol::spawn(echo(stream)).detach(); } }) } smol-2.0.1/examples/unix-signal.rs000066400000000000000000000016261465701726600171310ustar00rootroot00000000000000//! Uses the `signal-hook` crate to catch the Ctrl-C signal. //! //! Run with: //! //! ``` //! cargo run --example unix-signal //! ``` #[cfg(unix)] fn main() -> std::io::Result<()> { use std::os::unix::{io::AsRawFd, net::UnixStream}; use smol::{prelude::*, Async}; smol::block_on(async { // Create a Unix stream that receives a byte on each signal occurrence. let (a, mut b) = Async::::pair()?; // Async isn't IntoRawFd, but it is AsRawFd, so let's pass the raw fd directly. signal_hook::low_level::pipe::register_raw(signal_hook::consts::SIGINT, a.as_raw_fd())?; println!("Waiting for Ctrl-C..."); // Receive a byte that indicates the Ctrl-C signal occurred. b.read_exact(&mut [0]).await?; println!("Done!"); Ok(()) }) } #[cfg(not(unix))] fn main() { println!("This example works only on Unix systems!"); } smol-2.0.1/examples/web-crawler.rs000066400000000000000000000046431465701726600171070ustar00rootroot00000000000000//! Crawls the Rust language website and prints found pages. //! //! Run with: //! //! ``` //! cargo run --example web-crawler //! ``` use std::collections::{HashSet, VecDeque}; use anyhow::Result; use async_channel::{bounded, Sender}; use scraper::{Html, Selector}; const ROOT: &str = "https://www.rust-lang.org"; /// Fetches the HTML contents of a web page. async fn fetch(url: String, sender: Sender) { let body = surf::get(&url).recv_string().await; let body = body.unwrap_or_default(); sender.send(body).await.ok(); } /// Extracts links from a HTML body. fn links(body: String) -> Vec { let mut v = Vec::new(); for elem in Html::parse_fragment(&body).select(&Selector::parse("a").unwrap()) { if let Some(link) = elem.value().attr("href") { v.push(link.to_string()); } } v } fn main() -> Result<()> { smol::block_on(async { let mut seen = HashSet::new(); let mut queue = VecDeque::new(); seen.insert(ROOT.to_string()); queue.push_back(ROOT.to_string()); let (s, r) = bounded(200); let mut tasks = 0; // Loop while the queue is not empty or tasks are fetching pages. while queue.len() + tasks > 0 { // Limit the number of concurrent tasks. while tasks < s.capacity().unwrap() { // Process URLs in the queue and fetch more pages. match queue.pop_front() { None => break, Some(url) => { println!("{}", url); tasks += 1; smol::spawn(fetch(url, s.clone())).detach(); } } } // Get a fetched web page. let body = r.recv().await.unwrap(); tasks -= 1; // Parse links in the web page and add them to the queue. for mut url in links(body) { // Add the site prefix if it's missing. if url.starts_with('/') { url = format!("{}{}", ROOT, url); } // If the URL makes sense and was not seen already, push it into the queue. if url.starts_with(ROOT) && seen.insert(url.clone()) { url = url.trim_end_matches('/').to_string(); queue.push_back(url); } } } Ok(()) }) } smol-2.0.1/examples/websocket-client.rs000066400000000000000000000103351465701726600201320ustar00rootroot00000000000000//! A WebSocket+TLS client based on `async-tungstenite` and `async-native-tls`. //! //! First start a server: //! //! ``` //! cargo run --example websocket-server //! ``` //! //! Then start a client: //! //! ``` //! cargo run --example websocket-client //! ``` use std::net::{TcpStream, ToSocketAddrs}; use std::pin::Pin; use std::task::{Context, Poll}; use anyhow::{bail, Context as _, Result}; use async_native_tls::{Certificate, TlsConnector, TlsStream}; use async_tungstenite::{tungstenite, WebSocketStream}; use futures::sink::{Sink, SinkExt}; use smol::{prelude::*, Async}; use tungstenite::handshake::client::Response; use tungstenite::Message; use url::Url; /// Connects to a WebSocket address (optionally secured by TLS). async fn connect(addr: &str, tls: TlsConnector) -> Result<(WsStream, Response)> { // Parse the address. let url = Url::parse(addr)?; let host = url.host_str().context("cannot parse host")?.to_string(); let port = url.port_or_known_default().context("cannot guess port")?; // Resolve the address. let socket_addr = { let host = host.clone(); smol::unblock(move || (host.as_str(), port).to_socket_addrs()) .await? .next() .context("cannot resolve address")? }; // Connect to the address. match url.scheme() { "ws" => { let stream = Async::::connect(socket_addr).await?; let (stream, resp) = async_tungstenite::client_async(addr, stream).await?; Ok((WsStream::Plain(stream), resp)) } "wss" => { // In case of WSS, establish a secure TLS connection first. let stream = Async::::connect(socket_addr).await?; let stream = tls.connect(host, stream).await?; let (stream, resp) = async_tungstenite::client_async(addr, stream).await?; Ok((WsStream::Tls(stream), resp)) } scheme => bail!("unsupported scheme: {}", scheme), } } fn main() -> Result<()> { // Initialize TLS with the local certificate. let mut builder = native_tls::TlsConnector::builder(); builder.add_root_certificate(Certificate::from_pem(include_bytes!("certificate.pem"))?); let tls = TlsConnector::from(builder); smol::block_on(async { // Connect to the server. let (mut stream, resp) = connect("wss://127.0.0.1:9001", tls).await?; dbg!(resp); // Send a message and receive a response. stream.send(Message::text("Hello!")).await?; dbg!(stream.next().await); Ok(()) }) } /// A WebSocket or WebSocket+TLS connection. enum WsStream { /// A plain WebSocket connection. Plain(WebSocketStream>), /// A WebSocket connection secured by TLS. Tls(WebSocketStream>>), } impl Sink for WsStream { type Error = tungstenite::Error; fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_ready(cx), WsStream::Tls(s) => Pin::new(s).poll_ready(cx), } } fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> { match &mut *self { WsStream::Plain(s) => Pin::new(s).start_send(item), WsStream::Tls(s) => Pin::new(s).start_send(item), } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_flush(cx), WsStream::Tls(s) => Pin::new(s).poll_flush(cx), } } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_close(cx), WsStream::Tls(s) => Pin::new(s).poll_close(cx), } } } impl Stream for WsStream { type Item = tungstenite::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_next(cx), WsStream::Tls(s) => Pin::new(s).poll_next(cx), } } } smol-2.0.1/examples/websocket-server.rs000066400000000000000000000102201465701726600201530ustar00rootroot00000000000000//! A WebSocket+TLS echo server based on `async-tungstenite` and `async-native-tls`. //! //! First start a server: //! //! ``` //! cargo run --example websocket-server //! ``` //! //! Then start a client: //! //! ``` //! cargo run --example websocket-client //! ``` use std::net::{TcpListener, TcpStream}; use std::pin::Pin; use std::task::{Context, Poll}; use anyhow::{Context as _, Result}; use async_native_tls::{Identity, TlsAcceptor, TlsStream}; use async_tungstenite::{tungstenite, WebSocketStream}; use futures::sink::{Sink, SinkExt}; use smol::{future, prelude::*, Async}; use tungstenite::Message; /// Echoes messages from the client back to it. async fn echo(mut stream: WsStream) -> Result<()> { let msg = stream.next().await.context("expected a message")??; stream.send(Message::text(msg.to_string())).await?; Ok(()) } /// Listens for incoming connections and serves them. async fn listen(listener: Async, tls: Option) -> Result<()> { let host = match &tls { None => format!("ws://{}", listener.get_ref().local_addr()?), Some(_) => format!("wss://{}", listener.get_ref().local_addr()?), }; println!("Listening on {}", host); loop { // Accept the next connection. let (stream, _) = listener.accept().await?; println!("Accepted client: {}", stream.get_ref().peer_addr()?); match &tls { None => { let stream = WsStream::Plain(async_tungstenite::accept_async(stream).await?); smol::spawn(echo(stream)).detach(); } Some(tls) => { // In case of WSS, establish a secure TLS connection first. let stream = tls.accept(stream).await?; let stream = WsStream::Tls(async_tungstenite::accept_async(stream).await?); smol::spawn(echo(stream)).detach(); } } } } fn main() -> Result<()> { // Initialize TLS with the local certificate, private key, and password. let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?; let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start WS and WSS servers. smol::block_on(async { let ws = listen(Async::::bind(([127, 0, 0, 1], 9000))?, None); let wss = listen( Async::::bind(([127, 0, 0, 1], 9001))?, Some(tls), ); future::try_zip(ws, wss).await?; Ok(()) }) } /// A WebSocket or WebSocket+TLS connection. enum WsStream { /// A plain WebSocket connection. Plain(WebSocketStream>), /// A WebSocket connection secured by TLS. Tls(WebSocketStream>>), } impl Sink for WsStream { type Error = tungstenite::Error; fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_ready(cx), WsStream::Tls(s) => Pin::new(s).poll_ready(cx), } } fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> { match &mut *self { WsStream::Plain(s) => Pin::new(s).start_send(item), WsStream::Tls(s) => Pin::new(s).start_send(item), } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_flush(cx), WsStream::Tls(s) => Pin::new(s).poll_flush(cx), } } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_close(cx), WsStream::Tls(s) => Pin::new(s).poll_close(cx), } } } impl Stream for WsStream { type Item = tungstenite::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { WsStream::Plain(s) => Pin::new(s).poll_next(cx), WsStream::Tls(s) => Pin::new(s).poll_next(cx), } } } smol-2.0.1/examples/windows-uds.rs000066400000000000000000000065651465701726600171650ustar00rootroot00000000000000//! Uses the `uds_windows` crate to simulate Unix sockets on Windows. //! //! Run with: //! //! ``` //! cargo run --example windows-uds //! ``` #[cfg(windows)] fn main() -> std::io::Result<()> { use std::ops::Deref; use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket}; use std::path::PathBuf; use smol::{future, prelude::*, Async, Unblock}; use std::io; use tempfile::tempdir; // n.b.: notgull: uds_windows does not support I/O safety yet, hence the wrapper types struct UnixListener(uds_windows::UnixListener); impl From for UnixListener { fn from(ul: uds_windows::UnixListener) -> Self { Self(ul) } } impl Deref for UnixListener { type Target = uds_windows::UnixListener; fn deref(&self) -> &uds_windows::UnixListener { &self.0 } } impl AsSocket for UnixListener { fn as_socket(&self) -> BorrowedSocket<'_> { unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } struct UnixStream(uds_windows::UnixStream); impl From for UnixStream { fn from(ul: uds_windows::UnixStream) -> Self { Self(ul) } } impl Deref for UnixStream { type Target = uds_windows::UnixStream; fn deref(&self) -> &uds_windows::UnixStream { &self.0 } } impl AsSocket for UnixStream { fn as_socket(&self) -> BorrowedSocket<'_> { unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } impl io::Read for UnixStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { io::Read::read(&mut self.0, buf) } } impl io::Write for UnixStream { fn write(&mut self, buf: &[u8]) -> io::Result { io::Write::write(&mut self.0, buf) } fn flush(&mut self) -> io::Result<()> { io::Write::flush(&mut self.0) } } unsafe impl async_io::IoSafe for UnixStream {} async fn client(addr: PathBuf) -> io::Result<()> { // Connect to the address. let stream = Async::new(UnixStream::from(uds_windows::UnixStream::connect(addr)?))?; println!("Connected to {:?}", stream.get_ref().peer_addr()?); // Pipe the stream to stdout. let mut stdout = Unblock::new(std::io::stdout()); futures_lite::io::copy(stream, &mut stdout).await?; Ok(()) } let dir = tempdir()?; let path = dir.path().join("socket"); future::block_on(async { // Create a listener. let listener = Async::new(UnixListener::from(uds_windows::UnixListener::bind(&path)?))?; println!("Listening on {:?}", listener.get_ref().local_addr()?); future::try_zip( async { // Accept the client. let (stream, _) = listener.read_with(|l| l.accept()).await?; println!("Accepted a client"); // Send a message, drop the stream, and wait for the client. Async::new(UnixStream::from(stream))? .write_all(b"Hello!\n") .await?; Ok(()) }, client(path), ) .await?; Ok(()) }) } #[cfg(not(windows))] fn main() { println!("This example works only on Windows!"); } smol-2.0.1/src/000077500000000000000000000000001465701726600132715ustar00rootroot00000000000000smol-2.0.1/src/lib.rs000066400000000000000000000036711465701726600144140ustar00rootroot00000000000000//! A small and fast async runtime. //! //! This crate simply re-exports other smaller async crates (see the source). //! //! To use tokio-based libraries with smol, apply the [`async-compat`] adapter to futures and I/O //! types. //! //! # Examples //! //! Connect to an HTTP website, make a GET request, and pipe the response to the standard output: //! //! ``` //! use smol::{io, net, prelude::*, Unblock}; //! //! fn main() -> io::Result<()> { //! smol::block_on(async { //! let mut stream = net::TcpStream::connect("example.com:80").await?; //! let req = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"; //! stream.write_all(req).await?; //! //! let mut stdout = Unblock::new(std::io::stdout()); //! io::copy(stream, &mut stdout).await?; //! Ok(()) //! }) //! } //! ``` //! //! There's a lot more in the [examples] directory. //! //! [`async-compat`]: https://docs.rs/async-compat //! [examples]: https://github.com/smol-rs/smol/tree/master/examples //! [get-request]: https://github.com/smol-rs/smol/blob/master/examples/get-request.rs #![doc( html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" )] #![doc( html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" )] #![forbid(unsafe_code)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #[cfg(doctest)] doc_comment::doctest!("../README.md"); #[doc(inline)] pub use { async_executor::{Executor, LocalExecutor, Task}, async_io::{block_on, Async, Timer}, blocking::{unblock, Unblock}, futures_lite::{future, io, pin, prelude, ready, stream}, }; #[doc(inline)] pub use {async_channel as channel, async_fs as fs, async_lock as lock, async_net as net}; #[cfg(not(target_os = "espidf"))] #[doc(inline)] pub use async_process as process; mod spawn; pub use spawn::spawn; smol-2.0.1/src/spawn.rs000066400000000000000000000041171465701726600147720ustar00rootroot00000000000000use std::future::Future; use std::panic::catch_unwind; use std::thread; use async_executor::{Executor, Task}; use async_io::block_on; use async_lock::OnceCell; use futures_lite::future; /// Spawns a task onto the global executor (single-threaded by default). /// /// There is a global executor that gets lazily initialized on first use. It is included in this /// library for convenience when writing unit tests and small programs, but it is otherwise /// more advisable to create your own [`Executor`]. /// /// By default, the global executor is run by a single background thread, but you can also /// configure the number of threads by setting the `SMOL_THREADS` environment variable. /// /// Since the executor is kept around forever, `drop` is not called for tasks when the program /// exits. /// /// # Examples /// /// ``` /// let task = smol::spawn(async { /// 1 + 2 /// }); /// /// smol::block_on(async { /// assert_eq!(task.await, 3); /// }); /// ``` pub fn spawn(future: impl Future + Send + 'static) -> Task { static GLOBAL: OnceCell> = OnceCell::new(); fn global() -> &'static Executor<'static> { GLOBAL.get_or_init_blocking(|| { let num_threads = { // Parse SMOL_THREADS or default to 1. std::env::var("SMOL_THREADS") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(1) }; for n in 1..=num_threads { thread::Builder::new() .name(format!("smol-{}", n)) .spawn(|| loop { catch_unwind(|| block_on(global().run(future::pending::<()>()))).ok(); }) .expect("cannot spawn executor thread"); } // Prevent spawning another thread by running the process driver on this thread. let ex = Executor::new(); #[cfg(not(target_os = "espidf"))] ex.spawn(async_process::driver()).detach(); ex }) } global().spawn(future) }