зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1550903 - Part 2: Revendor dependencies. r=emilio,kats,froydnj
It was unhappy about the new LICENSE (fuchsia-cprng) but it's the same as the other fuschia crates. Since I don't think this is used at build time but has the same license as the other fuschia crates, I put it in the RUNTIME_LICENSE_PACKAGE_WHITELIST list. I also removed sha1 from that list as it's not used anymore Differential Revision: https://phabricator.services.mozilla.com/D30746 --HG-- rename : third_party/rust/httparse/Cargo.toml => third_party/rust/autocfg/Cargo.toml rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/autocfg/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/autocfg/LICENSE-MIT rename : third_party/rust/httparse/Cargo.toml => third_party/rust/cloudabi/Cargo.toml rename : third_party/rust/httparse/Cargo.toml => third_party/rust/fuchsia-cprng/Cargo.toml rename : third_party/rust/sha1/LICENSE => third_party/rust/fuchsia-cprng/LICENSE rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/mio-extras/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/mio-extras/LICENSE-MIT rename : third_party/rust/rand/.cargo-checksum.json => third_party/rust/rand-0.4.3/.cargo-checksum.json rename : third_party/rust/rand/CHANGELOG.md => third_party/rust/rand-0.4.3/CHANGELOG.md rename : third_party/rust/rand/Cargo.toml => third_party/rust/rand-0.4.3/Cargo.toml rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand-0.4.3/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand-0.4.3/LICENSE-MIT rename : third_party/rust/rand/README.md => third_party/rust/rand-0.4.3/README.md rename : third_party/rust/rand/appveyor.yml => third_party/rust/rand-0.4.3/appveyor.yml rename : third_party/rust/rand/benches/bench.rs => third_party/rust/rand-0.4.3/benches/bench.rs rename : third_party/rust/rand/benches/distributions/exponential.rs => third_party/rust/rand-0.4.3/benches/distributions/exponential.rs rename : third_party/rust/rand/benches/distributions/gamma.rs => third_party/rust/rand-0.4.3/benches/distributions/gamma.rs rename : third_party/rust/rand/benches/distributions/mod.rs => third_party/rust/rand-0.4.3/benches/distributions/mod.rs rename : third_party/rust/rand/benches/distributions/normal.rs => third_party/rust/rand-0.4.3/benches/distributions/normal.rs rename : third_party/rust/rand/benches/generators.rs => third_party/rust/rand-0.4.3/benches/generators.rs rename : third_party/rust/rand/benches/misc.rs => third_party/rust/rand-0.4.3/benches/misc.rs rename : third_party/rust/rand/src/distributions/exponential.rs => third_party/rust/rand-0.4.3/src/distributions/exponential.rs rename : third_party/rust/rand/src/distributions/gamma.rs => third_party/rust/rand-0.4.3/src/distributions/gamma.rs rename : third_party/rust/rand/src/distributions/mod.rs => third_party/rust/rand-0.4.3/src/distributions/mod.rs rename : third_party/rust/rand/src/distributions/normal.rs => third_party/rust/rand-0.4.3/src/distributions/normal.rs rename : third_party/rust/rand/src/distributions/range.rs => third_party/rust/rand-0.4.3/src/distributions/range.rs rename : third_party/rust/rand/src/distributions/ziggurat_tables.rs => third_party/rust/rand-0.4.3/src/distributions/ziggurat_tables.rs rename : third_party/rust/rand/src/jitter.rs => third_party/rust/rand-0.4.3/src/jitter.rs rename : third_party/rust/rand/src/lib.rs => third_party/rust/rand-0.4.3/src/lib.rs rename : third_party/rust/rand/src/os.rs => third_party/rust/rand-0.4.3/src/os.rs rename : third_party/rust/rand/src/prng/chacha.rs => third_party/rust/rand-0.4.3/src/prng/chacha.rs rename : third_party/rust/rand/src/prng/isaac.rs => third_party/rust/rand-0.4.3/src/prng/isaac.rs rename : third_party/rust/rand/src/prng/isaac64.rs => third_party/rust/rand-0.4.3/src/prng/isaac64.rs rename : third_party/rust/rand/src/prng/mod.rs => third_party/rust/rand-0.4.3/src/prng/mod.rs rename : third_party/rust/rand/src/prng/xorshift.rs => third_party/rust/rand-0.4.3/src/prng/xorshift.rs rename : third_party/rust/rand/src/rand_impls.rs => third_party/rust/rand-0.4.3/src/rand_impls.rs rename : third_party/rust/rand/src/read.rs => third_party/rust/rand-0.4.3/src/read.rs rename : third_party/rust/rand/src/reseeding.rs => third_party/rust/rand-0.4.3/src/reseeding.rs rename : third_party/rust/rand/src/seq.rs => third_party/rust/rand-0.4.3/src/seq.rs rename : third_party/rust/rand/utils/ziggurat_tables.py => third_party/rust/rand-0.4.3/utils/ziggurat_tables.py rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_chacha/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_chacha/LICENSE-MIT rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_core-0.3.1/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_core-0.3.1/LICENSE-MIT rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_core/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_core/LICENSE-MIT rename : third_party/rust/httparse/Cargo.toml => third_party/rust/rand_hc/Cargo.toml rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_hc/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_hc/LICENSE-MIT rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_isaac/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_isaac/LICENSE-MIT rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_jitter/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_jitter/LICENSE-MIT rename : third_party/rust/rand/src/jitter.rs => third_party/rust/rand_jitter/src/lib.rs rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_os/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_os/LICENSE-MIT rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_pcg/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_pcg/LICENSE-MIT rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/rand_xorshift/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/rand_xorshift/LICENSE-MIT rename : third_party/rust/httparse/Cargo.toml => third_party/rust/rdrand/Cargo.toml rename : third_party/rust/rand/LICENSE-APACHE => third_party/rust/sha-1/LICENSE-APACHE rename : third_party/rust/rand/LICENSE-MIT => third_party/rust/sha-1/LICENSE-MIT extra : moz-landing-system : lando
This commit is contained in:
Родитель
cf92185937
Коммит
8a3f3e785c
|
@ -165,6 +165,7 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore-
|
|||
'bindgen',
|
||||
'fuchsia-zircon',
|
||||
'fuchsia-zircon-sys',
|
||||
'fuchsia-cprng',
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -172,7 +173,6 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore-
|
|||
# license, but that also need to explicitly mentioned in about:license.
|
||||
RUNTIME_LICENSE_PACKAGE_WHITELIST = {
|
||||
'BSD-3-Clause': [
|
||||
'sha1',
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"59b541962c15f51087a11d7296e3b69ec6d9b8a8e9e8db949e7629ea28183564","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac","README.md":"d0e320dccace525a2cbcf3bc5d43ac9b7b61ee86b9c9713bf68993c3ad789d0a","examples/integers.rs":"589ff4271566dfa322becddf3e2c7b592e6e0bc97b02892ce75619b7e452e930","examples/paths.rs":"1b30e466b824ce8df7ad0a55334424131d9d2573d6cf9f7d5d50c09c8901d526","examples/traits.rs":"cbee6a3e1f7db60b02ae25b714926517144a77cb492021f492774cf0e1865a9e","examples/versions.rs":"38535e6d9f5bfae0de474a3db79a40e8f5da8ba9334c5ff4c363de9bc99d4d12","src/error.rs":"12de7dafea4a35d1dc2f0fa79bfa038386bbbea72bf083979f4ddf227999eeda","src/lib.rs":"7392068683dc86107bcf8073c81ff8fb7026f5fb98a355a4d47301c5946eeccb","src/tests.rs":"34bdceeffdbdd7f2535a6ed8272482b17325dba044c91bb96f3f8caeec58a83d","src/version.rs":"165324950f2195aaf068c47a4f2f0992b2bf18d8b7f4f17b6b264767523c0e5d"},"package":"a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"}
|
|
@ -0,0 +1,24 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "autocfg"
|
||||
version = "0.1.2"
|
||||
authors = ["Josh Stone <cuviper@gmail.com>"]
|
||||
description = "Automatic cfg for Rust compiler features"
|
||||
readme = "README.md"
|
||||
keywords = ["rustc", "build", "autoconf"]
|
||||
categories = ["development-tools::build-utils"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/cuviper/autocfg"
|
||||
|
||||
[dependencies]
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2018 Josh Stone
|
||||
|
||||
Permission 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.
|
|
@ -0,0 +1,68 @@
|
|||
autocfg
|
||||
=======
|
||||
|
||||
[![autocfg crate](https://img.shields.io/crates/v/autocfg.svg)](https://crates.io/crates/autocfg)
|
||||
[![autocfg documentation](https://docs.rs/autocfg/badge.svg)](https://docs.rs/autocfg)
|
||||
![minimum rustc 1.0](https://img.shields.io/badge/rustc-1.0+-red.svg)
|
||||
[![Travis Status](https://travis-ci.org/cuviper/autocfg.svg?branch=master)](https://travis-ci.org/cuviper/autocfg)
|
||||
|
||||
A Rust library for build scripts to automatically configure code based on
|
||||
compiler support. Code snippets are dynamically tested to see if the `rustc`
|
||||
will accept them, rather than hard-coding specific version support.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[build-dependencies]
|
||||
autocfg = "0.1"
|
||||
```
|
||||
|
||||
Then use it in your `build.rs` script to detect compiler features. For
|
||||
example, to test for 128-bit integer support, it might look like:
|
||||
|
||||
```rust
|
||||
extern crate autocfg;
|
||||
|
||||
fn main() {
|
||||
let ac = autocfg::new();
|
||||
ac.emit_has_type("i128");
|
||||
|
||||
// (optional) We don't need to rerun for anything external.
|
||||
autocfg::rerun_path(file!());
|
||||
}
|
||||
```
|
||||
|
||||
If the type test succeeds, this will write a `cargo:rustc-cfg=has_i128` line
|
||||
for Cargo, which translates to Rust arguments `--cfg has_i128`. Then in the
|
||||
rest of your Rust code, you can add `#[cfg(has_i128)]` conditions on code that
|
||||
should only be used when the compiler supports it.
|
||||
|
||||
|
||||
## Release Notes
|
||||
|
||||
- 0.1.2 (2018-01-16)
|
||||
- Add `rerun_env(ENV)` to print `cargo:rerun-if-env-changed=ENV`
|
||||
- Add `rerun_path(PATH)` to print `cargo:rerun-if-changed=PATH`
|
||||
|
||||
|
||||
## Minimum Rust version policy
|
||||
|
||||
This crate's minimum supported `rustc` version is `1.0.0`. Compatibility is
|
||||
its entire reason for existence, so this crate will be extremely conservative
|
||||
about raising this requirement. If this is ever deemed necessary, it will be
|
||||
treated as a major breaking change for semver purposes.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is 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.
|
|
@ -0,0 +1,9 @@
|
|||
extern crate autocfg;
|
||||
|
||||
fn main() {
|
||||
// Normally, cargo will set `OUT_DIR` for build scripts.
|
||||
let ac = autocfg::AutoCfg::with_dir("target").unwrap();
|
||||
for i in 3..8 {
|
||||
ac.emit_has_type(&format!("i{}", 1 << i));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
extern crate autocfg;
|
||||
|
||||
fn main() {
|
||||
// Normally, cargo will set `OUT_DIR` for build scripts.
|
||||
let ac = autocfg::AutoCfg::with_dir("target").unwrap();
|
||||
|
||||
// since ancient times...
|
||||
ac.emit_has_path("std::vec::Vec");
|
||||
ac.emit_path_cfg("std::vec::Vec", "has_vec");
|
||||
|
||||
// rustc 1.10.0
|
||||
ac.emit_has_path("std::panic::PanicInfo");
|
||||
ac.emit_path_cfg("std::panic::PanicInfo", "has_panic_info");
|
||||
|
||||
// rustc 1.20.0
|
||||
ac.emit_has_path("std::mem::ManuallyDrop");
|
||||
ac.emit_path_cfg("std::mem::ManuallyDrop", "has_manually_drop");
|
||||
|
||||
// rustc 1.25.0
|
||||
ac.emit_has_path("std::ptr::NonNull");
|
||||
ac.emit_path_cfg("std::ptr::NonNull", "has_non_null");
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
extern crate autocfg;
|
||||
|
||||
fn main() {
|
||||
// Normally, cargo will set `OUT_DIR` for build scripts.
|
||||
let ac = autocfg::AutoCfg::with_dir("target").unwrap();
|
||||
|
||||
// since ancient times...
|
||||
ac.emit_has_trait("std::ops::Add");
|
||||
ac.emit_trait_cfg("std::ops::Add", "has_ops");
|
||||
|
||||
// trait parameters have to be provided
|
||||
ac.emit_has_trait("std::borrow::Borrow<str>");
|
||||
ac.emit_trait_cfg("std::borrow::Borrow<str>", "has_borrow");
|
||||
|
||||
// rustc 1.8.0
|
||||
ac.emit_has_trait("std::ops::AddAssign");
|
||||
ac.emit_trait_cfg("std::ops::AddAssign", "has_assign_ops");
|
||||
|
||||
// rustc 1.12.0
|
||||
ac.emit_has_trait("std::iter::Sum");
|
||||
ac.emit_trait_cfg("std::iter::Sum", "has_sum");
|
||||
|
||||
// rustc 1.28.0
|
||||
ac.emit_has_trait("std::alloc::GlobalAlloc");
|
||||
ac.emit_trait_cfg("std::alloc::GlobalAlloc", "has_global_alloc");
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
extern crate autocfg;
|
||||
|
||||
fn main() {
|
||||
// Normally, cargo will set `OUT_DIR` for build scripts.
|
||||
let ac = autocfg::AutoCfg::with_dir("target").unwrap();
|
||||
for i in 0..100 {
|
||||
ac.emit_rustc_version(1, i);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
use std::error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::num;
|
||||
use std::str;
|
||||
|
||||
/// A common error type for the `autocfg` crate.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
"AutoCfg error"
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match self.kind {
|
||||
ErrorKind::Io(ref e) => Some(e),
|
||||
ErrorKind::Num(ref e) => Some(e),
|
||||
ErrorKind::Utf8(ref e) => Some(e),
|
||||
ErrorKind::Other(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match self.kind {
|
||||
ErrorKind::Io(ref e) => e.fmt(f),
|
||||
ErrorKind::Num(ref e) => e.fmt(f),
|
||||
ErrorKind::Utf8(ref e) => e.fmt(f),
|
||||
ErrorKind::Other(s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorKind {
|
||||
Io(io::Error),
|
||||
Num(num::ParseIntError),
|
||||
Utf8(str::Utf8Error),
|
||||
Other(&'static str),
|
||||
}
|
||||
|
||||
pub fn from_io(e: io::Error) -> Error {
|
||||
Error {
|
||||
kind: ErrorKind::Io(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_num(e: num::ParseIntError) -> Error {
|
||||
Error {
|
||||
kind: ErrorKind::Num(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_utf8(e: str::Utf8Error) -> Error {
|
||||
Error {
|
||||
kind: ErrorKind::Utf8(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &'static str) -> Error {
|
||||
Error {
|
||||
kind: ErrorKind::Other(s),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
//! A Rust library for build scripts to automatically configure code based on
|
||||
//! compiler support. Code snippets are dynamically tested to see if the `rustc`
|
||||
//! will accept them, rather than hard-coding specific version support.
|
||||
//!
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! Add this to your `Cargo.toml`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [build-dependencies]
|
||||
//! autocfg = "0.1"
|
||||
//! ```
|
||||
//!
|
||||
//! Then use it in your `build.rs` script to detect compiler features. For
|
||||
//! example, to test for 128-bit integer support, it might look like:
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate autocfg;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! # // Normally, cargo will set `OUT_DIR` for build scripts.
|
||||
//! # std::env::set_var("OUT_DIR", "target");
|
||||
//! let ac = autocfg::new();
|
||||
//! ac.emit_has_type("i128");
|
||||
//!
|
||||
//! // (optional) We don't need to rerun for anything external.
|
||||
//! autocfg::rerun_path(file!());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If the type test succeeds, this will write a `cargo:rustc-cfg=has_i128` line
|
||||
//! for Cargo, which translates to Rust arguments `--cfg has_i128`. Then in the
|
||||
//! rest of your Rust code, you can add `#[cfg(has_i128)]` conditions on code that
|
||||
//! should only be used when the compiler supports it.
|
||||
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
mod version;
|
||||
use version::Version;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Helper to detect compiler features for `cfg` output in build scripts.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AutoCfg {
|
||||
out_dir: PathBuf,
|
||||
rustc: PathBuf,
|
||||
rustc_version: Version,
|
||||
target: Option<OsString>,
|
||||
}
|
||||
|
||||
/// Writes a config flag for rustc on standard out.
|
||||
///
|
||||
/// This looks like: `cargo:rustc-cfg=CFG`
|
||||
///
|
||||
/// Cargo will use this in arguments to rustc, like `--cfg CFG`.
|
||||
pub fn emit(cfg: &str) {
|
||||
println!("cargo:rustc-cfg={}", cfg);
|
||||
}
|
||||
|
||||
/// Writes a line telling Cargo to rerun the build script if `path` changes.
|
||||
///
|
||||
/// This looks like: `cargo:rerun-if-changed=PATH`
|
||||
///
|
||||
/// This requires at least cargo 0.7.0, corresponding to rustc 1.6.0. Earlier
|
||||
/// versions of cargo will simply ignore the directive.
|
||||
pub fn rerun_path(path: &str) {
|
||||
println!("cargo:rerun-if-changed={}", path);
|
||||
}
|
||||
|
||||
/// Writes a line telling Cargo to rerun the build script if the environment
|
||||
/// variable `var` changes.
|
||||
///
|
||||
/// This looks like: `cargo:rerun-if-env-changed=VAR`
|
||||
///
|
||||
/// This requires at least cargo 0.21.0, corresponding to rustc 1.20.0. Earlier
|
||||
/// versions of cargo will simply ignore the directive.
|
||||
pub fn rerun_env(var: &str) {
|
||||
println!("cargo:rerun-if-env-changed={}", var);
|
||||
}
|
||||
|
||||
/// Create a new `AutoCfg` instance.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `AutoCfg::new()` returns an error.
|
||||
pub fn new() -> AutoCfg {
|
||||
AutoCfg::new().unwrap()
|
||||
}
|
||||
|
||||
impl AutoCfg {
|
||||
/// Create a new `AutoCfg` instance.
|
||||
///
|
||||
/// # Common errors
|
||||
///
|
||||
/// - `rustc` can't be executed, from `RUSTC` or in the `PATH`.
|
||||
/// - The version output from `rustc` can't be parsed.
|
||||
/// - `OUT_DIR` is not set in the environment, or is not a writable directory.
|
||||
///
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
match env::var_os("OUT_DIR") {
|
||||
Some(d) => Self::with_dir(d),
|
||||
None => Err(error::from_str("no OUT_DIR specified!")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `AutoCfg` instance with the specified output directory.
|
||||
///
|
||||
/// # Common errors
|
||||
///
|
||||
/// - `rustc` can't be executed, from `RUSTC` or in the `PATH`.
|
||||
/// - The version output from `rustc` can't be parsed.
|
||||
/// - `dir` is not a writable directory.
|
||||
///
|
||||
pub fn with_dir<T: Into<PathBuf>>(dir: T) -> Result<Self, Error> {
|
||||
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
|
||||
let rustc: PathBuf = rustc.into();
|
||||
let rustc_version = try!(Version::from_rustc(&rustc));
|
||||
|
||||
// Sanity check the output directory
|
||||
let dir = dir.into();
|
||||
let meta = try!(fs::metadata(&dir).map_err(error::from_io));
|
||||
if !meta.is_dir() || meta.permissions().readonly() {
|
||||
return Err(error::from_str("output path is not a writable directory"));
|
||||
}
|
||||
|
||||
Ok(AutoCfg {
|
||||
out_dir: dir,
|
||||
rustc: rustc,
|
||||
rustc_version: rustc_version,
|
||||
target: env::var_os("TARGET"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Test whether the current `rustc` reports a version greater than
|
||||
/// or equal to "`major`.`minor`".
|
||||
pub fn probe_rustc_version(&self, major: usize, minor: usize) -> bool {
|
||||
self.rustc_version >= Version::new(major, minor, 0)
|
||||
}
|
||||
|
||||
/// Sets a `cfg` value of the form `rustc_major_minor`, like `rustc_1_29`,
|
||||
/// if the current `rustc` is at least that version.
|
||||
pub fn emit_rustc_version(&self, major: usize, minor: usize) {
|
||||
if self.probe_rustc_version(major, minor) {
|
||||
emit(&format!("rustc_{}_{}", major, minor));
|
||||
}
|
||||
}
|
||||
|
||||
fn probe<T: AsRef<[u8]>>(&self, code: T) -> Result<bool, Error> {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
|
||||
static ID: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
let id = ID.fetch_add(1, Ordering::Relaxed);
|
||||
let mut command = Command::new(&self.rustc);
|
||||
command
|
||||
.arg(format!("--crate-name=probe{}", id))
|
||||
.arg("--crate-type=lib")
|
||||
.arg("--out-dir")
|
||||
.arg(&self.out_dir)
|
||||
.arg("--emit=llvm-ir");
|
||||
|
||||
if let Some(target) = self.target.as_ref() {
|
||||
command.arg("--target").arg(target);
|
||||
}
|
||||
|
||||
command.arg("-").stdin(Stdio::piped());
|
||||
let mut child = try!(command.spawn().map_err(error::from_io));
|
||||
try!(
|
||||
child
|
||||
.stdin
|
||||
.take()
|
||||
.expect("rustc stdin")
|
||||
.write_all(code.as_ref())
|
||||
.map_err(error::from_io)
|
||||
);
|
||||
|
||||
let status = try!(child.wait().map_err(error::from_io));
|
||||
Ok(status.success())
|
||||
}
|
||||
|
||||
/// Tests whether the given path can be used.
|
||||
///
|
||||
/// The test code is subject to change, but currently looks like:
|
||||
///
|
||||
/// ```ignore
|
||||
/// pub use PATH;
|
||||
/// ```
|
||||
pub fn probe_path(&self, path: &str) -> bool {
|
||||
self.probe(format!("pub use {};", path)).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Emits a config value `has_PATH` if `probe_path` returns true.
|
||||
///
|
||||
/// Any non-identifier characters in the `path` will be replaced with
|
||||
/// `_` in the generated config value.
|
||||
pub fn emit_has_path(&self, path: &str) {
|
||||
if self.probe_path(path) {
|
||||
emit(&format!("has_{}", mangle(path)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits the given `cfg` value if `probe_path` returns true.
|
||||
pub fn emit_path_cfg(&self, path: &str, cfg: &str) {
|
||||
if self.probe_path(path) {
|
||||
emit(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests whether the given trait can be used.
|
||||
///
|
||||
/// The test code is subject to change, but currently looks like:
|
||||
///
|
||||
/// ```ignore
|
||||
/// pub trait Probe: TRAIT + Sized {}
|
||||
/// ```
|
||||
pub fn probe_trait(&self, name: &str) -> bool {
|
||||
self.probe(format!("pub trait Probe: {} + Sized {{}}", name))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Emits a config value `has_TRAIT` if `probe_trait` returns true.
|
||||
///
|
||||
/// Any non-identifier characters in the trait `name` will be replaced with
|
||||
/// `_` in the generated config value.
|
||||
pub fn emit_has_trait(&self, name: &str) {
|
||||
if self.probe_trait(name) {
|
||||
emit(&format!("has_{}", mangle(name)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits the given `cfg` value if `probe_trait` returns true.
|
||||
pub fn emit_trait_cfg(&self, name: &str, cfg: &str) {
|
||||
if self.probe_trait(name) {
|
||||
emit(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests whether the given type can be used.
|
||||
///
|
||||
/// The test code is subject to change, but currently looks like:
|
||||
///
|
||||
/// ```ignore
|
||||
/// pub type Probe = TYPE;
|
||||
/// ```
|
||||
pub fn probe_type(&self, name: &str) -> bool {
|
||||
self.probe(format!("pub type Probe = {};", name))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Emits a config value `has_TYPE` if `probe_type` returns true.
|
||||
///
|
||||
/// Any non-identifier characters in the type `name` will be replaced with
|
||||
/// `_` in the generated config value.
|
||||
pub fn emit_has_type(&self, name: &str) {
|
||||
if self.probe_type(name) {
|
||||
emit(&format!("has_{}", mangle(name)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits the given `cfg` value if `probe_type` returns true.
|
||||
pub fn emit_type_cfg(&self, name: &str, cfg: &str) {
|
||||
if self.probe_type(name) {
|
||||
emit(cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mangle(s: &str) -> String {
|
||||
s.chars()
|
||||
.map(|c| match c {
|
||||
'A'...'Z' | 'a'...'z' | '0'...'9' => c,
|
||||
_ => '_',
|
||||
}).collect()
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
use super::AutoCfg;
|
||||
|
||||
#[test]
|
||||
fn autocfg_version() {
|
||||
let ac = AutoCfg::with_dir("target").unwrap();
|
||||
println!("version: {:?}", ac.rustc_version);
|
||||
assert!(ac.probe_rustc_version(1, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_cmp() {
|
||||
use super::version::Version;
|
||||
let v123 = Version::new(1, 2, 3);
|
||||
|
||||
assert!(Version::new(1, 0, 0) < v123);
|
||||
assert!(Version::new(1, 2, 2) < v123);
|
||||
assert!(Version::new(1, 2, 3) == v123);
|
||||
assert!(Version::new(1, 2, 4) > v123);
|
||||
assert!(Version::new(1, 10, 0) > v123);
|
||||
assert!(Version::new(2, 0, 0) > v123);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_add() {
|
||||
let ac = AutoCfg::with_dir("target").unwrap();
|
||||
assert!(ac.probe_path("std::ops::Add"));
|
||||
assert!(ac.probe_trait("std::ops::Add"));
|
||||
assert!(ac.probe_trait("std::ops::Add<i32>"));
|
||||
assert!(ac.probe_trait("std::ops::Add<i32, Output = i32>"));
|
||||
assert!(ac.probe_type("std::ops::Add<i32, Output = i32>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_as_ref() {
|
||||
let ac = AutoCfg::with_dir("target").unwrap();
|
||||
assert!(ac.probe_path("std::convert::AsRef"));
|
||||
assert!(ac.probe_trait("std::convert::AsRef<str>"));
|
||||
assert!(ac.probe_type("std::convert::AsRef<str>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_i128() {
|
||||
let ac = AutoCfg::with_dir("target").unwrap();
|
||||
let missing = !ac.probe_rustc_version(1, 26);
|
||||
assert!(missing ^ ac.probe_path("std::i128"));
|
||||
assert!(missing ^ ac.probe_type("i128"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_sum() {
|
||||
let ac = AutoCfg::with_dir("target").unwrap();
|
||||
let missing = !ac.probe_rustc_version(1, 12);
|
||||
assert!(missing ^ ac.probe_path("std::iter::Sum"));
|
||||
assert!(missing ^ ac.probe_trait("std::iter::Sum"));
|
||||
assert!(missing ^ ac.probe_trait("std::iter::Sum<i32>"));
|
||||
assert!(missing ^ ac.probe_type("std::iter::Sum<i32>"));
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
use super::{error, Error};
|
||||
|
||||
/// A version structure for making relative comparisons.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Version {
|
||||
major: usize,
|
||||
minor: usize,
|
||||
patch: usize,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
/// Creates a `Version` instance for a specific `major.minor.patch` version.
|
||||
pub fn new(major: usize, minor: usize, patch: usize) -> Self {
|
||||
Version {
|
||||
major: major,
|
||||
minor: minor,
|
||||
patch: patch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rustc(rustc: &Path) -> Result<Self, Error> {
|
||||
// Get rustc's verbose version
|
||||
let output = try!(
|
||||
Command::new(rustc)
|
||||
.args(&["--version", "--verbose"])
|
||||
.output()
|
||||
.map_err(error::from_io)
|
||||
);
|
||||
if !output.status.success() {
|
||||
return Err(error::from_str("could not execute rustc"));
|
||||
}
|
||||
let output = try!(str::from_utf8(&output.stdout).map_err(error::from_utf8));
|
||||
|
||||
// Find the release line in the verbose version output.
|
||||
let release = match output.lines().find(|line| line.starts_with("release: ")) {
|
||||
Some(line) => &line["release: ".len()..],
|
||||
None => return Err(error::from_str("could not find rustc release")),
|
||||
};
|
||||
|
||||
// Strip off any extra channel info, e.g. "-beta.N", "-nightly"
|
||||
let version = match release.find('-') {
|
||||
Some(i) => &release[..i],
|
||||
None => release,
|
||||
};
|
||||
|
||||
// Split the version into semver components.
|
||||
let mut iter = version.splitn(3, '.');
|
||||
let major = try!(iter.next().ok_or(error::from_str("missing major version")));
|
||||
let minor = try!(iter.next().ok_or(error::from_str("missing minor version")));
|
||||
let patch = try!(iter.next().ok_or(error::from_str("missing patch version")));
|
||||
|
||||
Ok(Version::new(
|
||||
try!(major.parse().map_err(error::from_num)),
|
||||
try!(minor.parse().map_err(error::from_num)),
|
||||
try!(patch.parse().map_err(error::from_num)),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"16ef935056e2aaf48b28c1340a63cc75febaabc901c752da9a3a2edbe081b429","bitflags.rs":"4621173dcf1307094cf240d26955b6f25c2f770dfd4e975ec2728771209006b5","cloudabi.rs":"93e139ba72a04db4934b04a6f21b054757a218a687e0bd2f6ba32514ec8c5f38"},"package":"ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"}
|
|
@ -0,0 +1,31 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
authors = ["Nuxi (https://nuxi.nl/) and contributors"]
|
||||
description = "Low level interface to CloudABI. Contains all syscalls and related types."
|
||||
homepage = "https://nuxi.nl/cloudabi/"
|
||||
documentation = "https://docs.rs/cloudabi/"
|
||||
keywords = ["cloudabi"]
|
||||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/nuxinl/cloudabi"
|
||||
|
||||
[lib]
|
||||
path = "cloudabi.rs"
|
||||
[dependencies.bitflags]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["bitflags"]
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2018 Nuxi (https://nuxi.nl/) and contributors.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
// Appease Rust's tidy.
|
||||
// ignore-license
|
||||
|
||||
#[cfg(feature = "bitflags")]
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
// Minimal implementation of bitflags! in case we can't depend on the bitflags
|
||||
// crate. Only implements `bits()` and a `from_bits_truncate()` that doesn't
|
||||
// actually truncate.
|
||||
#[cfg(not(feature = "bitflags"))]
|
||||
macro_rules! bitflags {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $name:ident: $type:ty {
|
||||
$($(#[$const_attr:meta])* const $const:ident = $val:expr;)*
|
||||
}
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct $name { bits: $type }
|
||||
impl $name {
|
||||
$($(#[$const_attr])* pub const $const: $name = $name{ bits: $val };)*
|
||||
pub fn bits(&self) -> $type { self.bits }
|
||||
pub fn from_bits_truncate(bits: $type) -> Self { $name{ bits } }
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1 @@
|
|||
{"files":{"AUTHORS":"f82920a5bcfc71b86c1de4be4cdea8af2009ba9e5735824f95ed5043a03e46f0","Cargo.toml":"bb2497b2907c3af499ce7ac3722dae044968e13409a372b7760150f8a01c74c5","LICENSE":"03b114f53e6587a398931762ee11e2395bfdba252a329940e2c8c9e81813845b","PATENTS":"52beb3ac72a0e7f5060384d16e4e6f91573016448fbff363c0b01a66fe99f547","src/lib.rs":"3d76c35c13203093ddf7ce2a3be5e98d768a8091cd9d99bd083fe8db35364096"},"package":"a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"}
|
|
@ -0,0 +1,10 @@
|
|||
# This is the list of Fuchsia Authors.
|
||||
|
||||
# Names should be added to this file as one of
|
||||
# Organization's name
|
||||
# Individual's name <submission email address>
|
||||
# Individual's name <submission email address> <email2> <emailN>
|
||||
|
||||
Google Inc.
|
||||
The Chromium Authors
|
||||
The Go Authors
|
|
@ -0,0 +1,22 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
authors = ["Erick Tryzelaar <etryzelaar@google.com>"]
|
||||
include = ["src/*.rs", "Cargo.toml", "AUTHORS", "LICENSE", "PATENTS"]
|
||||
description = "Rust crate for the Fuchsia cryptographically secure pseudorandom number generator"
|
||||
readme = "README.md"
|
||||
license-file = "LICENSE"
|
||||
repository = "https://fuchsia.googlesource.com/fuchsia/+/master/garnet/public/rust/fuchsia-cprng"
|
|
@ -1,24 +1,18 @@
|
|||
Copyright (c) 2014 by Armin Ronacher.
|
||||
|
||||
Copyright (c) 2013 Koka El Kiwi
|
||||
|
||||
Some rights reserved.
|
||||
Copyright 2019 The Fuchsia Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Fuchsia project.
|
||||
|
||||
Google 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, transfer, and otherwise run, modify and propagate the contents
|
||||
of this implementation of Fuchsia, where such license applies only to
|
||||
those patent claims, both currently owned by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by
|
||||
this implementation. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute
|
||||
or order or agree to the institution of patent litigation or any other
|
||||
patent enforcement activity against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that this
|
||||
implementation of Fuchsia constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of
|
||||
Fuchsia shall terminate as of the date such litigation is filed.
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2019 The Fuchsia Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//! Type-safe bindings for the Zircon kernel's CPRNG.
|
||||
|
||||
#![no_std]
|
||||
#![deny(warnings)]
|
||||
|
||||
/// Draw random bytes from the kernel's CPRNG to fill the given buffer.
|
||||
///
|
||||
/// Wraps the
|
||||
/// [zx_cprng_draw](https://fuchsia.googlesource.com/fuchsia/+/master/zircon/docs/syscalls/cprng_draw.md)
|
||||
/// syscall.
|
||||
pub fn cprng_draw(buffer: &mut [u8]) {
|
||||
unsafe { zx_cprng_draw(buffer.as_mut_ptr(), buffer.len()) };
|
||||
}
|
||||
|
||||
#[link(name = "zircon")]
|
||||
extern "C" {
|
||||
fn zx_cprng_draw(buffer: *mut u8, length: usize);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cprng() {
|
||||
let mut buffer = [0; 20];
|
||||
cprng_draw(&mut buffer);
|
||||
let mut first_zero = 0;
|
||||
let mut last_zero = 0;
|
||||
for _ in 0..30 {
|
||||
let mut buffer = [0; 20];
|
||||
cprng_draw(&mut buffer);
|
||||
if buffer[0] == 0 {
|
||||
first_zero += 1;
|
||||
}
|
||||
if buffer[19] == 0 {
|
||||
last_zero += 1;
|
||||
}
|
||||
}
|
||||
assert_ne!(first_zero, 30);
|
||||
assert_ne!(last_zero, 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cprng_large() {
|
||||
let mut buffer = [0; 1024];
|
||||
cprng_draw(&mut buffer);
|
||||
|
||||
for mut s in buffer.chunks_mut(256) {
|
||||
cprng_draw(&mut s);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"ce89721483a37a5e21e4c7c8bd36b64a03067c23b3c6a9569e552a2a0364522e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"ad5ce29918424008bdd8c6e7851bb064533456cbb32ec0d0955b2bc9444613d3","README.md":"8777517c90cff609e5a00a46b548f44803a4943cc7430a0ca6ddc4712af8d4ac","benches/parse.rs":"07f7d63b625090f1ccb086679e24c104de450d8733bb517e38a6eda8511e35e3","src/iter.rs":"9782faa1736e7988feceecdd5169f51b8b7926cbcf6611bbef9e018763e3cec8","src/lib.rs":"d95d4de02b19f2f921e13f0f477092d202fe0e707dc007b9bbd05282d7e69ebc"},"package":"af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"}
|
||||
{"files":{"Cargo.toml":"2d893bf5d1e9a5445e74c2a7a75b804492245647004155a798f5456e5c0f4712","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"ad5ce29918424008bdd8c6e7851bb064533456cbb32ec0d0955b2bc9444613d3","README.md":"b54e1e379daa12d26aadad7c1bef578a3cbcf243f2f229d8b17868c90a384d7d","benches/parse.rs":"caabd31cbcb00172bb7f31a1398dbed27a3bf36b01d69729c3d6fbd9a4008e05","build.rs":"00bab146ec6a10fac91958a06bdd4b7b4d71932e0480b6e84a4248cb339dbe79","src/iter.rs":"3f165be2f0d3bb7e0cbe37cb67560e36e06cdb5921e145446a5985c3580a6b6e","src/lib.rs":"21436cacd3b33b20ad75877e6260f34391a9215ffa6ad2c10ebf1a0c67ac07dc","src/macros.rs":"25de190f894eefb48a6992f9dc3d039d9065f908f381d683f0395d568d992275","src/simd/avx2.rs":"1b27cad7b5c210f8a1283d423ca18372127ea50f5565b9be0b83d2a51a1eb826","src/simd/fallback.rs":"0234cc11459f3225d6e3d329068a01206e39625a538bdc292e0d1beaa06a37e5","src/simd/mod.rs":"c67e118a45da679d41de0d51c2f632f64902083db4be8f636021dbfe08f0f9b4","src/simd/sse42.rs":"373f5bf0596942b9ba4497a8a89975117ec0da6d8b8c2345fe03e0d28e68627f","tests/uri.rs":"92a0dc4cfa8eee2e6b8e9109e2e0062ccb50b9ec8cb4b7c06794dab1a3e68c9c"},"package":"e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"}
|
|
@ -12,9 +12,14 @@
|
|||
|
||||
[package]
|
||||
name = "httparse"
|
||||
version = "1.2.3"
|
||||
authors = ["Sean McArthur <sean.monstar@gmail.com>"]
|
||||
version = "1.3.3"
|
||||
authors = ["Sean McArthur <sean@seanmonstar.com>"]
|
||||
build = "build.rs"
|
||||
description = "A tiny, safe, speedy, zero-copy HTTP/1.x parser."
|
||||
documentation = "https://docs.rs/httparse"
|
||||
readme = "README.md"
|
||||
keywords = ["http", "parser", "no_std"]
|
||||
categories = ["network-programming", "no-std", "parser-implementations", "web-programming"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/seanmonstar/httparse"
|
||||
[profile.bench]
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
[![Build Status](https://travis-ci.org/seanmonstar/httparse.svg?branch=master)](https://travis-ci.org/seanmonstar/httparse)
|
||||
[![Coverage Status](https://coveralls.io/repos/seanmonstar/httparse/badge.svg)](https://coveralls.io/r/seanmonstar/httparse)
|
||||
[![crates.io](http://meritbadge.herokuapp.com/httparse)](https://crates.io/crates/httparse)
|
||||
[![crates.io](https://img.shields.io/crates/v/httparse.svg)](https://crates.io/crates/httparse)
|
||||
|
||||
A push parser for the HTTP 1.x protocol. Avoids allocations. Fast.
|
||||
A push parser for the HTTP 1.x protocol. Avoids allocations. No copy. **Fast.**
|
||||
|
||||
Works with `no_std`, simply disable the `std` Cargo feature.
|
||||
|
||||
[Documentation](https://docs.rs/httparse)
|
||||
[Changelog](https://github.com/seanmonstar/httparse/releases)
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -15,12 +18,12 @@ let mut headers = [httparse::EMPTY_HEADER; 16];
|
|||
let mut req = httparse::Request::new(&mut headers);
|
||||
|
||||
let buf = b"GET /index.html HTTP/1.1\r\nHost";
|
||||
assert!(try!(req.parse(buf)).is_partial());
|
||||
assert!(req.parse(buf)?.is_partial());
|
||||
|
||||
// a partial request, so we try again once we have more data
|
||||
|
||||
let buf = b"GET /index.html HTTP/1.1\r\nHost: example.domain\r\n\r\n";
|
||||
assert!(try!(req.parse(buf)).is_complete());
|
||||
assert!(req.parse(buf)?.is_complete());
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
@ -5,6 +5,11 @@ extern crate httparse;
|
|||
|
||||
extern crate test;
|
||||
|
||||
const REQ_SHORT: &'static [u8] = b"\
|
||||
GET / HTTP/1.0\r\n\
|
||||
Host: example.com\r\n\
|
||||
Cookie: session=60; user_id=1\r\n\r\n";
|
||||
|
||||
const REQ: &'static [u8] = b"\
|
||||
GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\r\n\
|
||||
Host: www.kittyhell.com\r\n\
|
||||
|
@ -15,9 +20,7 @@ Accept-Encoding: gzip,deflate\r\n\
|
|||
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n\
|
||||
Keep-Alive: 115\r\n\
|
||||
Connection: keep-alive\r\n\
|
||||
Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral\r\n\r\n";
|
||||
|
||||
|
||||
Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n";
|
||||
|
||||
|
||||
#[bench]
|
||||
|
@ -68,3 +71,52 @@ fn bench_httparse(b: &mut test::Bencher) {
|
|||
});
|
||||
b.bytes = REQ.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pico_short(b: &mut test::Bencher) {
|
||||
use std::mem;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct Header<'a>(&'a [u8], &'a [u8]);
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
struct Headers<'a>(&'a mut [Header<'a>]);
|
||||
let method = [0i8; 16];
|
||||
let path = [0i8; 16];
|
||||
let mut minor_version = 0;
|
||||
let mut h = [Header(&[], &[]); 16];
|
||||
let mut h_len = h.len();
|
||||
let headers = Headers(&mut h);
|
||||
let prev_buf_len = 0;
|
||||
|
||||
b.iter(|| {
|
||||
let ret = unsafe {
|
||||
pico::ffi::phr_parse_request(
|
||||
REQ_SHORT.as_ptr() as *const _,
|
||||
REQ_SHORT.len(),
|
||||
&mut method.as_ptr(),
|
||||
&mut 16,
|
||||
&mut path.as_ptr(),
|
||||
&mut 16,
|
||||
&mut minor_version,
|
||||
mem::transmute::<*mut Header, *mut pico::ffi::phr_header>(headers.0.as_mut_ptr()),
|
||||
&mut h_len as *mut usize as *mut _,
|
||||
prev_buf_len
|
||||
)
|
||||
};
|
||||
assert_eq!(ret, REQ_SHORT.len() as i32);
|
||||
});
|
||||
b.bytes = REQ_SHORT.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_httparse_short(b: &mut test::Bencher) {
|
||||
let mut headers = [httparse::Header{ name: "", value: &[] }; 16];
|
||||
let mut req = httparse::Request::new(&mut headers);
|
||||
b.iter(|| {
|
||||
assert_eq!(req.parse(REQ_SHORT).unwrap(), httparse::Status::Complete(REQ_SHORT.len()));
|
||||
});
|
||||
b.bytes = REQ_SHORT.len() as u64;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc"));
|
||||
let output = Command::new(&rustc)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.expect("failed to check 'rustc --version'")
|
||||
.stdout;
|
||||
|
||||
let version = String::from_utf8(output)
|
||||
.expect("rustc version output should be utf-8");
|
||||
|
||||
enable_new_features(&version);
|
||||
}
|
||||
|
||||
fn enable_new_features(raw_version: &str) {
|
||||
let version = match Version::parse(raw_version) {
|
||||
Ok(version) => version,
|
||||
Err(err) => {
|
||||
println!("cargo:warning=failed to parse `rustc --version`: {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
enable_simd(version);
|
||||
}
|
||||
|
||||
fn enable_simd(version: Version) {
|
||||
if env::var_os("CARGO_FEATURE_STD").is_none() {
|
||||
println!("cargo:warning=building for no_std disables httparse SIMD");
|
||||
return;
|
||||
}
|
||||
|
||||
let env_disable = "CARGO_CFG_HTTPARSE_DISABLE_SIMD";
|
||||
if env::var_os(env_disable).is_some() {
|
||||
println!("cargo:warning=detected {} environment variable, disabling SIMD", env_disable);
|
||||
return;
|
||||
}
|
||||
|
||||
let min_simd_version = Version {
|
||||
major: 1,
|
||||
minor: 27,
|
||||
patch: 0,
|
||||
};
|
||||
|
||||
if version >= min_simd_version {
|
||||
println!("cargo:rustc-cfg=httparse_simd");
|
||||
}
|
||||
|
||||
// cfg(target_feature) isn't stable yet, but CARGO_CFG_TARGET_FEATURE has
|
||||
// a list... We aren't doing anything unsafe, since the is_x86_feature_detected
|
||||
// macro still checks in the actual lib, BUT!
|
||||
//
|
||||
// By peeking at the list here, we can change up slightly how we do feature
|
||||
// detection in the lib. If our features aren't in the feature list, we
|
||||
// stick with a cached runtime detection strategy.
|
||||
//
|
||||
// But if the features *are* in the list, we benefit from removing our cache,
|
||||
// since the compiler will eliminate several branches with its internal
|
||||
// cfg(target_feature) usage.
|
||||
|
||||
|
||||
let env_runtime_only = "CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME";
|
||||
if env::var_os(env_runtime_only).is_some() {
|
||||
println!("cargo:warning=detected {} environment variable, using runtime SIMD detection only", env_runtime_only);
|
||||
return;
|
||||
}
|
||||
let feature_list = match env::var_os("CARGO_CFG_TARGET_FEATURE") {
|
||||
Some(var) => match var.into_string() {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not valid utf-8");
|
||||
return;
|
||||
},
|
||||
},
|
||||
None => {
|
||||
println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not set");
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
let mut saw_sse42 = false;
|
||||
let mut saw_avx2 = false;
|
||||
|
||||
for feature in feature_list.split(',') {
|
||||
let feature = feature.trim();
|
||||
if !saw_sse42 && feature == "sse4.2" {
|
||||
saw_sse42 = true;
|
||||
println!("cargo:rustc-cfg=httparse_simd_target_feature_sse42");
|
||||
}
|
||||
|
||||
if !saw_avx2 && feature == "avx2" {
|
||||
saw_avx2 = true;
|
||||
println!("cargo:rustc-cfg=httparse_simd_target_feature_avx2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
struct Version {
|
||||
major: u32,
|
||||
minor: u32,
|
||||
patch: u32,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
fn parse(mut s: &str) -> Result<Version, String> {
|
||||
if !s.starts_with("rustc ") {
|
||||
return Err(format!("unrecognized version string: {}", s));
|
||||
}
|
||||
s = &s["rustc ".len()..];
|
||||
|
||||
let parts: Vec<&str> = s.split(".").collect();
|
||||
if parts.len() < 3 {
|
||||
return Err(format!("not enough version parts: {:?}", parts));
|
||||
}
|
||||
|
||||
let mut num = String::new();
|
||||
for c in parts[0].chars() {
|
||||
if !c.is_digit(10) {
|
||||
break;
|
||||
}
|
||||
num.push(c);
|
||||
}
|
||||
let major = try!(num.parse::<u32>().map_err(|e| e.to_string()));
|
||||
|
||||
num.clear();
|
||||
for c in parts[1].chars() {
|
||||
if !c.is_digit(10) {
|
||||
break;
|
||||
}
|
||||
num.push(c);
|
||||
}
|
||||
let minor = try!(num.parse::<u32>().map_err(|e| e.to_string()));
|
||||
|
||||
num.clear();
|
||||
for c in parts[2].chars() {
|
||||
if !c.is_digit(10) {
|
||||
break;
|
||||
}
|
||||
num.push(c);
|
||||
}
|
||||
let patch = try!(num.parse::<u32>().map_err(|e| e.to_string()));
|
||||
|
||||
Ok(Version {
|
||||
major: major,
|
||||
minor: minor,
|
||||
patch: patch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -25,10 +25,18 @@ impl<'a> Bytes<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bump(&mut self) {
|
||||
pub unsafe fn bump(&mut self) {
|
||||
debug_assert!(self.pos + 1 <= self.slice.len(), "overflow");
|
||||
self.pos += 1;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[inline]
|
||||
pub unsafe fn advance(&mut self, n: usize) {
|
||||
debug_assert!(self.pos + n <= self.slice.len(), "overflow");
|
||||
self.pos += n;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.slice.len()
|
||||
|
@ -36,21 +44,22 @@ impl<'a> Bytes<'a> {
|
|||
|
||||
#[inline]
|
||||
pub fn slice(&mut self) -> &'a [u8] {
|
||||
self.slice_skip(0)
|
||||
// not moving position at all, so it's safe
|
||||
unsafe {
|
||||
self.slice_skip(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn slice_skip(&mut self, skip: usize) -> &'a [u8] {
|
||||
pub unsafe fn slice_skip(&mut self, skip: usize) -> &'a [u8] {
|
||||
debug_assert!(self.pos >= skip);
|
||||
let head_pos = self.pos - skip;
|
||||
unsafe {
|
||||
let ptr = self.slice.as_ptr();
|
||||
let head = slice::from_raw_parts(ptr, head_pos);
|
||||
let tail = slice::from_raw_parts(ptr.offset(self.pos as isize), self.slice.len() - self.pos);
|
||||
self.pos = 0;
|
||||
self.slice = tail;
|
||||
head
|
||||
}
|
||||
let ptr = self.slice.as_ptr();
|
||||
let head = slice::from_raw_parts(ptr, head_pos);
|
||||
let tail = slice::from_raw_parts(ptr.offset(self.pos as isize), self.slice.len() - self.pos);
|
||||
self.pos = 0;
|
||||
self.slice = tail;
|
||||
head
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -63,6 +72,13 @@ impl<'a> Bytes<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<[u8]> for Bytes<'a> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.slice[self.pos..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Bytes<'a> {
|
||||
type Item = u8;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![doc(html_root_url = "https://docs.rs/httparse/1.3.3")]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
//! # httparse
|
||||
//!
|
||||
//! A push library for parsing HTTP/1.x requests and responses.
|
||||
|
@ -10,44 +12,25 @@
|
|||
//! parsing internals use an `Iterator` instead of direct indexing, while
|
||||
//! skipping bounds checks.
|
||||
//!
|
||||
//! The speed is faster than picohttpparser, when SIMD is not available.
|
||||
#[cfg(feature = "std")] extern crate std as core;
|
||||
//! With Rust 1.27.0 or later, support for SIMD is enabled automatically.
|
||||
//! If building an executable to be run on multiple platforms, and thus
|
||||
//! not passing `target_feature` or `target_cpu` flags to the compiler,
|
||||
//! runtime detection can still detect SSE4.2 or AVX2 support to provide
|
||||
//! massive wins.
|
||||
//!
|
||||
//! If compiling for a specific target, remembering to include
|
||||
//! `-C target_cpu=native` allows the detection to become compile time checks,
|
||||
//! making it *even* faster.
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std as core;
|
||||
|
||||
use core::{fmt, result, str, slice};
|
||||
|
||||
use iter::Bytes;
|
||||
|
||||
mod iter;
|
||||
|
||||
macro_rules! next {
|
||||
($bytes:ident) => ({
|
||||
match $bytes.next() {
|
||||
Some(b) => b,
|
||||
None => return Ok(Status::Partial)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! expect {
|
||||
($bytes:ident.next() == $pat:pat => $ret:expr) => {
|
||||
expect!(next!($bytes) => $pat |? $ret)
|
||||
};
|
||||
($e:expr => $pat:pat |? $ret:expr) => {
|
||||
match $e {
|
||||
v@$pat => v,
|
||||
_ => return $ret
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! complete {
|
||||
($e:expr) => {
|
||||
match try!($e) {
|
||||
Status::Complete(v) => v,
|
||||
Status::Partial => return Ok(Status::Partial)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[macro_use] mod macros;
|
||||
mod simd;
|
||||
|
||||
#[inline]
|
||||
fn shrink<T>(slice: &mut &mut [T], len: usize) {
|
||||
|
@ -70,10 +53,41 @@ fn shrink<T>(slice: &mut &mut [T], len: usize) {
|
|||
fn is_token(b: u8) -> bool {
|
||||
b > 0x1F && b < 0x7F
|
||||
}
|
||||
macro_rules! byte_map {
|
||||
($($flag:expr,)*) => ([
|
||||
$($flag != 0,)*
|
||||
])
|
||||
|
||||
// ASCII codes to accept URI string.
|
||||
// i.e. A-Z a-z 0-9 !#$%&'*+-._();:@=,/?[]~^
|
||||
// TODO: Make a stricter checking for URI string?
|
||||
static URI_MAP: [bool; 256] = byte_map![
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// \0 \n
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// commands
|
||||
0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
// \w ! " # $ % & ' ( ) * + , - . /
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
|
||||
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
// @ A B C D E F G H I J K L M N O
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
|
||||
// P Q R S T U V W X Y Z [ \ ] ^ _
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
// ` a b c d e f g h i j k l m n o
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
// p q r s t u v w x y z { | } ~ del
|
||||
// ====== Extended ASCII (aka. obs-text) ======
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
#[inline]
|
||||
fn is_uri_token(b: u8) -> bool {
|
||||
URI_MAP[b as usize]
|
||||
}
|
||||
|
||||
static HEADER_NAME_MAP: [bool; 256] = byte_map![
|
||||
|
@ -101,7 +115,7 @@ fn is_header_name_token(b: u8) -> bool {
|
|||
}
|
||||
|
||||
static HEADER_VALUE_MAP: [bool; 256] = byte_map![
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
|
@ -125,29 +139,6 @@ fn is_header_value_token(b: u8) -> bool {
|
|||
HEADER_VALUE_MAP[b as usize]
|
||||
}
|
||||
|
||||
|
||||
macro_rules! space {
|
||||
($bytes:ident or $err:expr) => ({
|
||||
expect!($bytes.next() == b' ' => Err($err));
|
||||
$bytes.slice();
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! newline {
|
||||
($bytes:ident) => ({
|
||||
match next!($bytes) {
|
||||
b'\r' => {
|
||||
expect!($bytes.next() == b'\n' => Err(Error::NewLine));
|
||||
$bytes.slice();
|
||||
},
|
||||
b'\n' => {
|
||||
$bytes.slice();
|
||||
},
|
||||
_ => return Err(Error::NewLine)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// An error in parsing.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -311,7 +302,7 @@ impl<'h, 'b> Request<'h, 'b> {
|
|||
let mut bytes = Bytes::new(buf);
|
||||
complete!(skip_empty_lines(&mut bytes));
|
||||
self.method = Some(complete!(parse_token(&mut bytes)));
|
||||
self.path = Some(complete!(parse_token(&mut bytes)));
|
||||
self.path = Some(complete!(parse_uri(&mut bytes)));
|
||||
self.version = Some(complete!(parse_version(&mut bytes)));
|
||||
newline!(bytes);
|
||||
|
||||
|
@ -328,11 +319,13 @@ fn skip_empty_lines(bytes: &mut Bytes) -> Result<()> {
|
|||
let b = bytes.peek();
|
||||
match b {
|
||||
Some(b'\r') => {
|
||||
bytes.bump();
|
||||
// there's `\r`, so it's safe to bump 1 pos
|
||||
unsafe { bytes.bump() };
|
||||
expect!(bytes.next() == b'\n' => Err(Error::NewLine));
|
||||
},
|
||||
Some(b'\n') => {
|
||||
bytes.bump();
|
||||
// there's `\n`, so it's safe to bump 1 pos
|
||||
unsafe { bytes.bump(); }
|
||||
},
|
||||
Some(..) => {
|
||||
bytes.slice();
|
||||
|
@ -396,6 +389,7 @@ impl<'h, 'b> Response<'h, 'b> {
|
|||
},
|
||||
b'\r' => {
|
||||
expect!(bytes.next() == b'\n' => Err(Error::Status));
|
||||
bytes.slice();
|
||||
self.reason = Some("");
|
||||
},
|
||||
b'\n' => self.reason = Some(""),
|
||||
|
@ -448,10 +442,21 @@ fn parse_version(bytes: &mut Bytes) -> Result<u8> {
|
|||
b'1' => 1,
|
||||
_ => return Err(Error::Version)
|
||||
};
|
||||
Ok(Status::Complete(v))
|
||||
} else {
|
||||
Ok(Status::Partial)
|
||||
return Ok(Status::Complete(v))
|
||||
}
|
||||
|
||||
// else (but not in `else` because of borrow checker)
|
||||
|
||||
// If there aren't at least 8 bytes, we still want to detect early
|
||||
// if this is a valid version or not. If it is, we'll return Partial.
|
||||
expect!(bytes.next() == b'H' => Err(Error::Version));
|
||||
expect!(bytes.next() == b'T' => Err(Error::Version));
|
||||
expect!(bytes.next() == b'T' => Err(Error::Version));
|
||||
expect!(bytes.next() == b'P' => Err(Error::Version));
|
||||
expect!(bytes.next() == b'/' => Err(Error::Version));
|
||||
expect!(bytes.next() == b'1' => Err(Error::Version));
|
||||
expect!(bytes.next() == b'.' => Err(Error::Version));
|
||||
Ok(Status::Partial)
|
||||
}
|
||||
|
||||
/// From [RFC 7230](https://tools.ietf.org/html/rfc7230):
|
||||
|
@ -508,6 +513,24 @@ fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
|
||||
simd::match_uri_vectored(bytes);
|
||||
|
||||
loop {
|
||||
let b = next!(bytes);
|
||||
if b == b' ' {
|
||||
return Ok(Status::Complete(unsafe {
|
||||
// all bytes up till `i` must have been `is_token`.
|
||||
str::from_utf8_unchecked(bytes.slice_skip(1))
|
||||
}));
|
||||
} else if !is_uri_token(b) {
|
||||
return Err(Error::Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn parse_code(bytes: &mut Bytes) -> Result<u16> {
|
||||
let hundreds = expect!(bytes.next() == b'0'...b'9' => Err(Error::Status));
|
||||
|
@ -515,8 +538,8 @@ fn parse_code(bytes: &mut Bytes) -> Result<u16> {
|
|||
let ones = expect!(bytes.next() == b'0'...b'9' => Err(Error::Status));
|
||||
|
||||
Ok(Status::Complete((hundreds - b'0') as u16 * 100 +
|
||||
(tens - b'0') as u16 * 10 +
|
||||
(ones - b'0') as u16))
|
||||
(tens - b'0') as u16 * 10 +
|
||||
(ones - b'0') as u16))
|
||||
}
|
||||
|
||||
/// Parse a buffer of bytes as headers.
|
||||
|
@ -544,6 +567,7 @@ pub fn parse_headers<'b: 'h, 'h>(src: &'b [u8], mut dst: &'h mut [Header<'b>])
|
|||
Ok(Status::Complete((pos, dst)))
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn parse_headers_iter<'a, 'b>(headers: &mut &mut [Header<'a>], bytes: &'b mut Bytes<'a>)
|
||||
-> Result<usize> {
|
||||
|
@ -609,7 +633,7 @@ fn parse_headers_iter<'a, 'b>(headers: &mut &mut [Header<'a>], bytes: &'b mut By
|
|||
|
||||
// parse value till EOL
|
||||
|
||||
|
||||
simd::match_header_value_vectored(bytes);
|
||||
|
||||
macro_rules! check {
|
||||
($bytes:ident, $i:ident) => ({
|
||||
|
@ -641,17 +665,31 @@ fn parse_headers_iter<'a, 'b>(headers: &mut &mut [Header<'a>], bytes: &'b mut By
|
|||
}
|
||||
|
||||
//found_ctl
|
||||
if b == b'\r' {
|
||||
let value_slice : &[u8] = if b == b'\r' {
|
||||
expect!(bytes.next() == b'\n' => Err(Error::HeaderValue));
|
||||
count += bytes.pos();
|
||||
header.value = bytes.slice_skip(2);
|
||||
// having just check that `\r\n` exists, it's safe to skip those 2 bytes
|
||||
unsafe {
|
||||
bytes.slice_skip(2)
|
||||
}
|
||||
} else if b == b'\n' {
|
||||
count += bytes.pos();
|
||||
header.value = bytes.slice_skip(1);
|
||||
// having just check that `\r\n` exists, it's safe to skip 1 byte
|
||||
unsafe {
|
||||
bytes.slice_skip(1)
|
||||
}
|
||||
} else {
|
||||
return Err(Error::HeaderValue);
|
||||
};
|
||||
// trim trailing whitespace in the header
|
||||
if let Some(last_visible) = value_slice.iter().rposition(|b| *b != b' ' && *b != b'\t' ) {
|
||||
// There is at least one non-whitespace character.
|
||||
header.value = &value_slice[0..last_visible+1];
|
||||
} else {
|
||||
// There is no non-whitespace character. This can only happen when value_slice is
|
||||
// empty.
|
||||
header.value = value_slice;
|
||||
}
|
||||
|
||||
}
|
||||
} // drop iter
|
||||
|
||||
|
@ -672,7 +710,7 @@ fn parse_headers_iter<'a, 'b>(headers: &mut &mut [Header<'a>], bytes: &'b mut By
|
|||
/// Ok(httparse::Status::Complete((3, 4))));
|
||||
/// ```
|
||||
pub fn parse_chunk_size(buf: &[u8])
|
||||
-> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
|
||||
-> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
|
||||
const RADIX: u64 = 16;
|
||||
let mut bytes = Bytes::new(buf);
|
||||
let mut size = 0;
|
||||
|
@ -682,7 +720,7 @@ pub fn parse_chunk_size(buf: &[u8])
|
|||
loop {
|
||||
let b = next!(bytes);
|
||||
match b {
|
||||
b'0'...b'9' if in_chunk_size => {
|
||||
b'0' ... b'9' if in_chunk_size => {
|
||||
if count > 15 {
|
||||
return Err(InvalidChunkSize);
|
||||
}
|
||||
|
@ -690,7 +728,7 @@ pub fn parse_chunk_size(buf: &[u8])
|
|||
size *= RADIX;
|
||||
size += (b - b'0') as u64;
|
||||
},
|
||||
b'a'...b'f' if in_chunk_size => {
|
||||
b'a' ... b'f' if in_chunk_size => {
|
||||
if count > 15 {
|
||||
return Err(InvalidChunkSize);
|
||||
}
|
||||
|
@ -698,7 +736,7 @@ pub fn parse_chunk_size(buf: &[u8])
|
|||
size *= RADIX;
|
||||
size += (b + 10 - b'a') as u64;
|
||||
}
|
||||
b'A'...b'F' if in_chunk_size => {
|
||||
b'A' ... b'F' if in_chunk_size => {
|
||||
if count > 15 {
|
||||
return Err(InvalidChunkSize);
|
||||
}
|
||||
|
@ -785,6 +823,28 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_simple_with_query_params,
|
||||
b"GET /thing?data=a HTTP/1.1\r\n\r\n",
|
||||
|req| {
|
||||
assert_eq!(req.method.unwrap(), "GET");
|
||||
assert_eq!(req.path.unwrap(), "/thing?data=a");
|
||||
assert_eq!(req.version.unwrap(), 1);
|
||||
assert_eq!(req.headers.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_simple_with_whatwg_query_params,
|
||||
b"GET /thing?data=a^ HTTP/1.1\r\n\r\n",
|
||||
|req| {
|
||||
assert_eq!(req.method.unwrap(), "GET");
|
||||
assert_eq!(req.path.unwrap(), "/thing?data=a^");
|
||||
assert_eq!(req.version.unwrap(), 1);
|
||||
assert_eq!(req.headers.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_headers,
|
||||
b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n",
|
||||
|
@ -800,6 +860,63 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_headers_optional_whitespace,
|
||||
b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n",
|
||||
|req| {
|
||||
assert_eq!(req.method.unwrap(), "GET");
|
||||
assert_eq!(req.path.unwrap(), "/");
|
||||
assert_eq!(req.version.unwrap(), 1);
|
||||
assert_eq!(req.headers.len(), 2);
|
||||
assert_eq!(req.headers[0].name, "Host");
|
||||
assert_eq!(req.headers[0].value, b"foo.com");
|
||||
assert_eq!(req.headers[1].name, "Cookie");
|
||||
assert_eq!(req.headers[1].value, b"");
|
||||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
// test the scalar parsing
|
||||
test_request_header_value_htab_short,
|
||||
b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\r\n\r\n",
|
||||
|req| {
|
||||
assert_eq!(req.method.unwrap(), "GET");
|
||||
assert_eq!(req.path.unwrap(), "/");
|
||||
assert_eq!(req.version.unwrap(), 1);
|
||||
assert_eq!(req.headers.len(), 1);
|
||||
assert_eq!(req.headers[0].name, "User-Agent");
|
||||
assert_eq!(req.headers[0].value, b"some\tagent");
|
||||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
// test the sse42 parsing
|
||||
test_request_header_value_htab_med,
|
||||
b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\r\n\r\n",
|
||||
|req| {
|
||||
assert_eq!(req.method.unwrap(), "GET");
|
||||
assert_eq!(req.path.unwrap(), "/");
|
||||
assert_eq!(req.version.unwrap(), 1);
|
||||
assert_eq!(req.headers.len(), 1);
|
||||
assert_eq!(req.headers[0].name, "User-Agent");
|
||||
assert_eq!(req.headers[0].value, b"1234567890some\tagent");
|
||||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
// test the avx2 parsing
|
||||
test_request_header_value_htab_long,
|
||||
b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\r\n\r\n",
|
||||
|req| {
|
||||
assert_eq!(req.method.unwrap(), "GET");
|
||||
assert_eq!(req.path.unwrap(), "/");
|
||||
assert_eq!(req.version.unwrap(), 1);
|
||||
assert_eq!(req.headers.len(), 1);
|
||||
assert_eq!(req.headers[0].name, "User-Agent");
|
||||
assert_eq!(req.headers[0].value, &b"1234567890some\t1234567890agent1234567890"[..]);
|
||||
}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_headers_max,
|
||||
b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
|
||||
|
@ -829,6 +946,12 @@ mod tests {
|
|||
|_req| {}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_partial_version,
|
||||
b"GET / HTTP/1.", Ok(Status::Partial),
|
||||
|_req| {}
|
||||
}
|
||||
|
||||
req! {
|
||||
test_request_newlines,
|
||||
b"GET / HTTP/1.1\nHost: foo.bar\n\n",
|
||||
|
@ -864,6 +987,14 @@ mod tests {
|
|||
|_r| {}
|
||||
}
|
||||
|
||||
|
||||
req! {
|
||||
test_request_with_invalid_but_short_version,
|
||||
b"GET / HTTP/1!",
|
||||
Err(::Error::Version),
|
||||
|_r| {}
|
||||
}
|
||||
|
||||
macro_rules! res {
|
||||
($name:ident, $buf:expr, |$arg:ident| $body:expr) => (
|
||||
res! {$name, $buf, Ok(Status::Complete($buf.len())), |$arg| $body }
|
||||
|
@ -920,6 +1051,19 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
res! {
|
||||
test_response_reason_missing_no_space_with_headers,
|
||||
b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n",
|
||||
|res| {
|
||||
assert_eq!(res.version.unwrap(), 1);
|
||||
assert_eq!(res.code.unwrap(), 200);
|
||||
assert_eq!(res.reason.unwrap(), "");
|
||||
assert_eq!(res.headers.len(), 1);
|
||||
assert_eq!(res.headers[0].name, "Foo");
|
||||
assert_eq!(res.headers[0].value, b"bar");
|
||||
}
|
||||
}
|
||||
|
||||
res! {
|
||||
test_response_reason_with_space_and_tab,
|
||||
b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
///! Utility macros
|
||||
|
||||
macro_rules! next {
|
||||
($bytes:ident) => ({
|
||||
match $bytes.next() {
|
||||
Some(b) => b,
|
||||
None => return Ok(Status::Partial)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! expect {
|
||||
($bytes:ident.next() == $pat:pat => $ret:expr) => {
|
||||
expect!(next!($bytes) => $pat |? $ret)
|
||||
};
|
||||
($e:expr => $pat:pat |? $ret:expr) => {
|
||||
match $e {
|
||||
v@$pat => v,
|
||||
_ => return $ret
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! complete {
|
||||
($e:expr) => {
|
||||
match try!($e) {
|
||||
Status::Complete(v) => v,
|
||||
Status::Partial => return Ok(Status::Partial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! byte_map {
|
||||
($($flag:expr,)*) => ([
|
||||
$($flag != 0,)*
|
||||
])
|
||||
}
|
||||
|
||||
macro_rules! space {
|
||||
($bytes:ident or $err:expr) => ({
|
||||
expect!($bytes.next() == b' ' => Err($err));
|
||||
$bytes.slice();
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! newline {
|
||||
($bytes:ident) => ({
|
||||
match next!($bytes) {
|
||||
b'\r' => {
|
||||
expect!($bytes.next() == b'\n' => Err(Error::NewLine));
|
||||
$bytes.slice();
|
||||
},
|
||||
b'\n' => {
|
||||
$bytes.slice();
|
||||
},
|
||||
_ => return Err(Error::NewLine)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
use ::iter::Bytes;
|
||||
|
||||
pub enum Scan {
|
||||
/// Returned when an implementation finds a noteworthy token.
|
||||
Found,
|
||||
/// Returned when an implementation couldn't keep running because the input was too short.
|
||||
TooShort,
|
||||
}
|
||||
|
||||
|
||||
pub unsafe fn parse_uri_batch_32<'a>(bytes: &mut Bytes<'a>) -> Scan {
|
||||
while bytes.as_ref().len() >= 32 {
|
||||
let advance = match_url_char_32_avx(bytes.as_ref());
|
||||
bytes.advance(advance);
|
||||
|
||||
if advance != 32 {
|
||||
return Scan::Found;
|
||||
}
|
||||
}
|
||||
Scan::TooShort
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[target_feature(enable = "avx2")]
|
||||
#[inline]
|
||||
#[allow(non_snake_case, overflowing_literals)]
|
||||
unsafe fn match_url_char_32_avx(buf: &[u8]) -> usize {
|
||||
debug_assert!(buf.len() >= 32);
|
||||
|
||||
/*
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
*/
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
let ptr = buf.as_ptr();
|
||||
|
||||
let LSH: __m256i = _mm256_set1_epi8(0x0f);
|
||||
let URI: __m256i = _mm256_setr_epi8(
|
||||
0xb8, 0xfc, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0x7c, 0x54, 0x7c, 0xd4, 0x7c,
|
||||
0xb8, 0xfc, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0x7c, 0x54, 0x7c, 0xd4, 0x7c,
|
||||
);
|
||||
let ARF: __m256i = _mm256_setr_epi8(
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
);
|
||||
|
||||
let data = _mm256_lddqu_si256(ptr as *const _);
|
||||
let rbms = _mm256_shuffle_epi8(URI, data);
|
||||
let cols = _mm256_and_si256(LSH, _mm256_srli_epi16(data, 4));
|
||||
let bits = _mm256_and_si256(_mm256_shuffle_epi8(ARF, cols), rbms);
|
||||
|
||||
let v = _mm256_cmpeq_epi8(bits, _mm256_setzero_si256());
|
||||
let r = 0xffffffff_00000000 | _mm256_movemask_epi8(v) as u64;
|
||||
|
||||
_tzcnt_u64(r) as usize
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe fn match_url_char_32_avx(_: &[u8]) -> usize {
|
||||
unreachable!("AVX2 detection should be disabled for x86");
|
||||
}
|
||||
|
||||
pub unsafe fn match_header_value_batch_32(bytes: &mut Bytes) -> Scan {
|
||||
while bytes.as_ref().len() >= 32 {
|
||||
let advance = match_header_value_char_32_avx(bytes.as_ref());
|
||||
bytes.advance(advance);
|
||||
|
||||
if advance != 32 {
|
||||
return Scan::Found;
|
||||
}
|
||||
}
|
||||
Scan::TooShort
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[target_feature(enable = "avx2")]
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn match_header_value_char_32_avx(buf: &[u8]) -> usize {
|
||||
debug_assert!(buf.len() >= 32);
|
||||
|
||||
/*
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
*/
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
let ptr = buf.as_ptr();
|
||||
|
||||
// %x09 %x20-%x7e %x80-%xff
|
||||
let TAB: __m256i = _mm256_set1_epi8(0x09);
|
||||
let DEL: __m256i = _mm256_set1_epi8(0x7f);
|
||||
let LOW: __m256i = _mm256_set1_epi8(0x1f);
|
||||
|
||||
let dat = _mm256_lddqu_si256(ptr as *const _);
|
||||
let low = _mm256_cmpgt_epi8(dat, LOW);
|
||||
let tab = _mm256_cmpeq_epi8(dat, TAB);
|
||||
let del = _mm256_cmpeq_epi8(dat, DEL);
|
||||
let bit = _mm256_andnot_si256(del, _mm256_or_si256(low, tab));
|
||||
let rev = _mm256_cmpeq_epi8(bit, _mm256_setzero_si256());
|
||||
let res = 0xffffffff_00000000 | _mm256_movemask_epi8(rev) as u64;
|
||||
|
||||
_tzcnt_u64(res) as usize
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe fn match_header_value_char_32_avx(_: &[u8]) -> usize {
|
||||
unreachable!("AVX2 detection should be disabled for x86");
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
use ::iter::Bytes;
|
||||
|
||||
// Fallbacks that do nothing...
|
||||
|
||||
#[inline(always)]
|
||||
pub fn match_uri_vectored(_: &mut Bytes) {}
|
||||
#[inline(always)]
|
||||
pub fn match_header_value_vectored(_: &mut Bytes) {}
|
|
@ -0,0 +1,238 @@
|
|||
#[cfg(not(all(
|
||||
httparse_simd,
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
)))]
|
||||
mod fallback;
|
||||
|
||||
#[cfg(not(all(
|
||||
httparse_simd,
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
)))]
|
||||
pub use self::fallback::*;
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
mod sse42;
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
any(
|
||||
httparse_simd_target_feature_avx2,
|
||||
not(httparse_simd_target_feature_sse42),
|
||||
),
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
mod avx2;
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
not(any(
|
||||
httparse_simd_target_feature_sse42,
|
||||
httparse_simd_target_feature_avx2,
|
||||
)),
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
mod runtime {
|
||||
//! Runtime detection of simd features. Used when the build script
|
||||
//! doesn't notice any target features at build time.
|
||||
//!
|
||||
//! While `is_x86_feature_detected!` has it's own caching built-in,
|
||||
//! at least in 1.27.0, the functions don't inline, leaving using it
|
||||
//! actually *slower* than just using the scalar fallback.
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
|
||||
static FEATURE: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
const INIT: usize = 0;
|
||||
const SSE_42: usize = 1;
|
||||
const AVX_2: usize = 2;
|
||||
const AVX_2_AND_SSE_42: usize = 3;
|
||||
const NONE: usize = ::core::usize::MAX;
|
||||
|
||||
fn detect() -> usize {
|
||||
let feat = FEATURE.load(Ordering::Relaxed);
|
||||
if feat == INIT {
|
||||
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
|
||||
if is_x86_feature_detected!("sse4.2") {
|
||||
FEATURE.store(AVX_2_AND_SSE_42, Ordering::Relaxed);
|
||||
return AVX_2_AND_SSE_42;
|
||||
} else {
|
||||
FEATURE.store(AVX_2, Ordering::Relaxed);
|
||||
return AVX_2;
|
||||
}
|
||||
} else if is_x86_feature_detected!("sse4.2") {
|
||||
FEATURE.store(SSE_42, Ordering::Relaxed);
|
||||
return SSE_42;
|
||||
} else {
|
||||
FEATURE.store(NONE, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
feat
|
||||
}
|
||||
|
||||
pub fn match_uri_vectored(bytes: &mut ::Bytes) {
|
||||
unsafe {
|
||||
match detect() {
|
||||
SSE_42 => super::sse42::parse_uri_batch_16(bytes),
|
||||
AVX_2 => { super::avx2::parse_uri_batch_32(bytes); },
|
||||
AVX_2_AND_SSE_42 => {
|
||||
if let super::avx2::Scan::Found = super::avx2::parse_uri_batch_32(bytes) {
|
||||
return;
|
||||
}
|
||||
super::sse42::parse_uri_batch_16(bytes)
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
// else do nothing
|
||||
}
|
||||
|
||||
pub fn match_header_value_vectored(bytes: &mut ::Bytes) {
|
||||
unsafe {
|
||||
match detect() {
|
||||
SSE_42 => super::sse42::match_header_value_batch_16(bytes),
|
||||
AVX_2 => { super::avx2::match_header_value_batch_32(bytes); },
|
||||
AVX_2_AND_SSE_42 => {
|
||||
if let super::avx2::Scan::Found = super::avx2::match_header_value_batch_32(bytes) {
|
||||
return;
|
||||
}
|
||||
super::sse42::match_header_value_batch_16(bytes)
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
// else do nothing
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
not(any(
|
||||
httparse_simd_target_feature_sse42,
|
||||
httparse_simd_target_feature_avx2,
|
||||
)),
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
pub use self::runtime::*;
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
httparse_simd_target_feature_sse42,
|
||||
not(httparse_simd_target_feature_avx2),
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
mod sse42_compile_time {
|
||||
pub fn match_uri_vectored(bytes: &mut ::Bytes) {
|
||||
if is_x86_feature_detected!("sse4.2") {
|
||||
unsafe {
|
||||
super::sse42::parse_uri_batch_16(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// else do nothing
|
||||
}
|
||||
|
||||
pub fn match_header_value_vectored(bytes: &mut ::Bytes) {
|
||||
if is_x86_feature_detected!("sse4.2") {
|
||||
unsafe {
|
||||
super::sse42::match_header_value_batch_16(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// else do nothing
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
httparse_simd_target_feature_sse42,
|
||||
not(httparse_simd_target_feature_avx2),
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
pub use self::sse42_compile_time::*;
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
httparse_simd_target_feature_avx2,
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
mod avx2_compile_time {
|
||||
pub fn match_uri_vectored(bytes: &mut ::Bytes) {
|
||||
// do both, since avx2 only works when bytes.len() >= 32
|
||||
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
|
||||
unsafe {
|
||||
super::avx2::parse_uri_batch_32(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
if is_x86_feature_detected!("sse4.2") {
|
||||
unsafe {
|
||||
super::sse42::parse_uri_batch_16(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// else do nothing
|
||||
}
|
||||
|
||||
pub fn match_header_value_vectored(bytes: &mut ::Bytes) {
|
||||
// do both, since avx2 only works when bytes.len() >= 32
|
||||
if cfg!(target_arch = "x86_64") && is_x86_feature_detected!("avx2") {
|
||||
let scanned = unsafe {
|
||||
super::avx2::match_header_value_batch_32(bytes)
|
||||
};
|
||||
|
||||
if let super::avx2::Scan::Found = scanned {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if is_x86_feature_detected!("sse4.2") {
|
||||
unsafe {
|
||||
super::sse42::match_header_value_batch_16(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// else do nothing
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
httparse_simd,
|
||||
httparse_simd_target_feature_avx2,
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
),
|
||||
))]
|
||||
pub use self::avx2_compile_time::*;
|
|
@ -0,0 +1,84 @@
|
|||
use ::iter::Bytes;
|
||||
|
||||
pub unsafe fn parse_uri_batch_16<'a>(bytes: &mut Bytes<'a>) {
|
||||
while bytes.as_ref().len() >= 16 {
|
||||
let advance = match_url_char_16_sse(bytes.as_ref());
|
||||
bytes.advance(advance);
|
||||
|
||||
if advance != 16 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.2")]
|
||||
#[allow(non_snake_case, overflowing_literals)]
|
||||
unsafe fn match_url_char_16_sse(buf: &[u8]) -> usize {
|
||||
debug_assert!(buf.len() >= 16);
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
let ptr = buf.as_ptr();
|
||||
|
||||
let LSH: __m128i = _mm_set1_epi8(0x0f);
|
||||
let URI: __m128i = _mm_setr_epi8(
|
||||
0xb8, 0xfc, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0x7c, 0x54, 0x7c, 0xd4, 0x7c,
|
||||
);
|
||||
let ARF: __m128i = _mm_setr_epi8(
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
);
|
||||
|
||||
let data = _mm_lddqu_si128(ptr as *const _);
|
||||
let rbms = _mm_shuffle_epi8(URI, data);
|
||||
let cols = _mm_and_si128(LSH, _mm_srli_epi16(data, 4));
|
||||
let bits = _mm_and_si128(_mm_shuffle_epi8(ARF, cols), rbms);
|
||||
|
||||
let v = _mm_cmpeq_epi8(bits, _mm_setzero_si128());
|
||||
let r = 0xffff_0000 | _mm_movemask_epi8(v) as u32;
|
||||
|
||||
_tzcnt_u32(r) as usize
|
||||
}
|
||||
|
||||
pub unsafe fn match_header_value_batch_16(bytes: &mut Bytes) {
|
||||
while bytes.as_ref().len() >= 16 {
|
||||
let advance = match_header_value_char_16_sse(bytes.as_ref());
|
||||
bytes.advance(advance);
|
||||
|
||||
if advance != 16 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.2")]
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn match_header_value_char_16_sse(buf: &[u8]) -> usize {
|
||||
debug_assert!(buf.len() >= 16);
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
let ptr = buf.as_ptr();
|
||||
|
||||
// %x09 %x20-%x7e %x80-%xff
|
||||
let TAB: __m128i = _mm_set1_epi8(0x09);
|
||||
let DEL: __m128i = _mm_set1_epi8(0x7f);
|
||||
let LOW: __m128i = _mm_set1_epi8(0x1f);
|
||||
|
||||
let dat = _mm_lddqu_si128(ptr as *const _);
|
||||
let low = _mm_cmpgt_epi8(dat, LOW);
|
||||
let tab = _mm_cmpeq_epi8(dat, TAB);
|
||||
let del = _mm_cmpeq_epi8(dat, DEL);
|
||||
let bit = _mm_andnot_si128(del, _mm_or_si128(low, tab));
|
||||
let rev = _mm_cmpeq_epi8(bit, _mm_setzero_si128());
|
||||
let res = 0xffff_0000 | _mm_movemask_epi8(rev) as u32;
|
||||
|
||||
_tzcnt_u32(res) as usize
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"0ac479629dc1cde30432141baf12e6ee347b14bd3cd07a44ea3e645fdb1a0d7c","Cargo.toml":"152d2f4435ce45bd83a0f3842a04c0aed7223f458c843c9ad1cc10ad6f0acca6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"4d3959aeee87b7924faecca7e7369370a2ac603422e7bb3fea86191b2574899d","README.md":"ea7850a3e9cc388dd40c7e5ea26e08cec788219fc4cd01280ea52cb7382d184f","src/lib.rs":"226ef2985de8085b4edd780578f4cf5e77f58512c566b2fa8462086db8f96e15"},"package":"a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"}
|
||||
{"files":{"CHANGELOG.md":"0aa4629e399d08ad3a446366d465449a5e92ba95aac708be76961100a9c7df48","Cargo.toml":"15c8e40c3c9ed8635ed338b633d343702b6f4a90dcd5aa759673d31af401dd0a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"03f6ccb4e6040abccf12b31551bbbd1800a5069a17950bbd6db850d85744800f","README.md":"c0798b2c9b0d3b9c0da509fdd9001eb2d80e6ed0850d312e36136ac269dbe49f","src/lib.rs":"7ea882864c246d78be6ea3558b92a8a7dd5732f55556cd641efb7a8caf4e64e3"},"package":"b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"}
|
|
@ -1,3 +1,49 @@
|
|||
<a name="v1.2.1"></a>
|
||||
## v1.2.1 (2018-12-03)
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* Implement Clone for LazyCell and AtomicLazyCell ([30fe4a8f](https://github.com/indiv0/lazycell/commit/30fe4a8f568059b3c78ed149a810962a676cb2b2))
|
||||
|
||||
|
||||
|
||||
<a name="v1.2.0"></a>
|
||||
## v1.2.0 (2018-09-19)
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* add `LazyCell::replace` for infallible access ([a63ffb90](https://github.com/indiv0/lazycell/commit/a63ffb9040a5e0683a9bbf9d3d5ef589f2ca8b7c))
|
||||
|
||||
|
||||
|
||||
<a name="v1.1.0"></a>
|
||||
## v1.1.0 (2018-09-10)
|
||||
|
||||
|
||||
#### Documentation
|
||||
|
||||
* add note regarding LazyCell::borrow_mut ([9d634d1f](https://github.com/indiv0/lazycell/commit/9d634d1fd9a21b7aa075d407bedf9fe77ba8b79f))
|
||||
* describe mutability more consistently ([b8078029](https://github.com/indiv0/lazycell/commit/b80780294611e92efddcdd33a701b3049ab5c5eb), closes [#78](https://github.com/indiv0/lazycell/issues/78))
|
||||
|
||||
#### Improvements
|
||||
|
||||
* add NONE constant for an empty AtomicLazyCell ([31aff0da](https://github.com/indiv0/lazycell/commit/31aff0dacf824841c5f38ef4acf0aa71ec4c36eb), closes [#87](https://github.com/indiv0/lazycell/issues/87))
|
||||
* add `LazyCell::borrow_mut_with` and `LazyCell::try_borrow_mut_with` ([fdc6d268](https://github.com/indiv0/lazycell/commit/fdc6d268f0e9a6668768302f45fe2bb4aa9a7c34), closes [#79](https://github.com/indiv0/lazycell/issues/79), [#80](https://github.com/indiv0/lazycell/issues/80))
|
||||
|
||||
|
||||
|
||||
<a name="v1.0.0"></a>
|
||||
## v1.0.0 (2018-06-06)
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* Add #![no_std] ([e59f6b55](https://github.com/indiv0/lazycell/commit/e59f6b5531e310d3df26b0eb40b1431937f38096))
|
||||
|
||||
|
||||
|
||||
<a name="0.6.0"></a>
|
||||
## 0.6.0 (2017-11-25)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "lazycell"
|
||||
version = "0.6.0"
|
||||
version = "1.2.1"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>", "Nikita Pekin <contact@nikitapek.in>"]
|
||||
include = ["CHANGELOG.md", "Cargo.toml", "LICENSE-MIT", "LICENSE-APACHE", "README.md", "src/**/*.rs"]
|
||||
description = "A library providing a lazily filled Cell struct"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Original work Copyright (c) 2014 The Rust Project Developers
|
||||
Modified work Copyright (c) 2016-2017 Nikita Pekin and lazycell contributors
|
||||
Modified work Copyright (c) 2016-2018 Nikita Pekin and lazycell contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
|
|
@ -30,7 +30,7 @@ Add the following to your `Cargo.toml`:
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
lazycell = "0.6"
|
||||
lazycell = "1.2"
|
||||
```
|
||||
|
||||
And in your `lib.rs` or `main.rs`:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Original work Copyright (c) 2014 The Rust Project Developers
|
||||
// Modified work Copyright (c) 2016-2017 Nikita Pekin and the lazycell contributors
|
||||
// Modified work Copyright (c) 2016-2018 Nikita Pekin and the lazycell contributors
|
||||
// See the README.md file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
|
@ -8,21 +8,20 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(feature = "nightly", feature(plugin))]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy))]
|
||||
|
||||
//! This crate provides a `LazyCell` struct which acts as a lazily filled
|
||||
//! `Cell`, but with frozen contents.
|
||||
//! `Cell`.
|
||||
//!
|
||||
//! With a `RefCell`, the inner contents cannot be borrowed for the lifetime of
|
||||
//! the entire object, but only of the borrows returned. A `LazyCell` is a
|
||||
//! variation on `RefCell` which allows borrows to be tied to the lifetime of
|
||||
//! the outer object.
|
||||
//!
|
||||
//! The limitation of a `LazyCell` is that after it is initialized and shared,
|
||||
//! it can be modified.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The following example shows a quick example of the basic functionality of
|
||||
|
@ -42,12 +41,23 @@
|
|||
//! ```
|
||||
//!
|
||||
//! `AtomicLazyCell` is a variant that uses an atomic variable to manage
|
||||
//! coordination in a thread-safe fashion.
|
||||
//! coordination in a thread-safe fashion. The limitation of an `AtomicLazyCell`
|
||||
//! is that after it is initialized, it can't be modified.
|
||||
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[macro_use]
|
||||
extern crate core as std;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::mem;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
/// A lazily filled `Cell`, with frozen contents.
|
||||
/// A lazily filled `Cell`, with mutable contents.
|
||||
///
|
||||
/// A `LazyCell` is completely frozen once filled, **unless** you have `&mut`
|
||||
/// access to it, in which case `LazyCell::borrow_mut` may be used to mutate the
|
||||
/// contents.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LazyCell<T> {
|
||||
inner: UnsafeCell<Option<T>>,
|
||||
|
@ -63,7 +73,7 @@ impl<T> LazyCell<T> {
|
|||
///
|
||||
/// This function will return `Err(value)` is the cell is already full.
|
||||
pub fn fill(&self, value: T) -> Result<(), T> {
|
||||
let mut slot = unsafe { &mut *self.inner.get() };
|
||||
let slot = unsafe { &mut *self.inner.get() };
|
||||
if slot.is_some() {
|
||||
return Err(value);
|
||||
}
|
||||
|
@ -72,6 +82,20 @@ impl<T> LazyCell<T> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Put a value into this cell.
|
||||
///
|
||||
/// Note that this function is infallible but requires `&mut self`. By
|
||||
/// requiring `&mut self` we're guaranteed that no active borrows to this
|
||||
/// cell can exist so we can always fill in the value. This may not always
|
||||
/// be usable, however, as `&mut self` may not be possible to borrow.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// This function returns the previous value, if any.
|
||||
pub fn replace(&mut self, value: T) -> Option<T> {
|
||||
mem::replace(unsafe { &mut *self.inner.get() }, Some(value))
|
||||
}
|
||||
|
||||
/// Test whether this cell has been previously filled.
|
||||
pub fn filled(&self) -> bool {
|
||||
self.borrow().is_some()
|
||||
|
@ -115,6 +139,26 @@ impl<T> LazyCell<T> {
|
|||
self.borrow().unwrap()
|
||||
}
|
||||
|
||||
/// Borrows the contents of this `LazyCell` mutably for the duration of the
|
||||
/// cell itself.
|
||||
///
|
||||
/// If the cell has not yet been filled, the cell is first filled using the
|
||||
/// function provided.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the cell becomes filled as a side effect of `f`.
|
||||
pub fn borrow_mut_with<F: FnOnce() -> T>(&mut self, f: F) -> &mut T {
|
||||
if !self.filled() {
|
||||
let value = f();
|
||||
if self.fill(value).is_err() {
|
||||
panic!("borrow_mut_with: cell was filled by closure")
|
||||
}
|
||||
}
|
||||
|
||||
self.borrow_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Same as `borrow_with`, but allows the initializing function to fail.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -133,8 +177,30 @@ impl<T> LazyCell<T> {
|
|||
Ok(self.borrow().unwrap())
|
||||
}
|
||||
|
||||
/// Same as `borrow_mut_with`, but allows the initializing function to fail.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the cell becomes filled as a side effect of `f`.
|
||||
pub fn try_borrow_mut_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
|
||||
where F: FnOnce() -> Result<T, E>
|
||||
{
|
||||
if self.filled() {
|
||||
return Ok(self.borrow_mut().unwrap());
|
||||
}
|
||||
let value = f()?;
|
||||
if self.fill(value).is_err() {
|
||||
panic!("try_borrow_mut_with: cell was filled by closure")
|
||||
}
|
||||
Ok(self.borrow_mut().unwrap())
|
||||
}
|
||||
|
||||
/// Consumes this `LazyCell`, returning the underlying value.
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
// Rust 1.25 changed UnsafeCell::into_inner() from unsafe to safe
|
||||
// function. This unsafe can be removed when supporting Rust older than
|
||||
// 1.25 is not needed.
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { self.inner.into_inner() }
|
||||
}
|
||||
}
|
||||
|
@ -149,12 +215,23 @@ impl<T: Copy> LazyCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T: Clone> Clone for LazyCell<T> {
|
||||
/// Create a clone of this `LazyCell`
|
||||
///
|
||||
/// If self has not been initialized, returns an uninitialized `LazyCell`
|
||||
/// otherwise returns a `LazyCell` already initialized with a clone of the
|
||||
/// contents of self.
|
||||
fn clone(&self) -> LazyCell<T> {
|
||||
LazyCell { inner: UnsafeCell::new(self.borrow().map(Clone::clone) ) }
|
||||
}
|
||||
}
|
||||
|
||||
// Tracks the AtomicLazyCell inner state
|
||||
const NONE: usize = 0;
|
||||
const LOCK: usize = 1;
|
||||
const SOME: usize = 2;
|
||||
|
||||
/// A lazily filled `Cell`, with frozen contents.
|
||||
/// A lazily filled and thread-safe `Cell`, with frozen contents.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AtomicLazyCell<T> {
|
||||
inner: UnsafeCell<Option<T>>,
|
||||
|
@ -162,12 +239,15 @@ pub struct AtomicLazyCell<T> {
|
|||
}
|
||||
|
||||
impl<T> AtomicLazyCell<T> {
|
||||
/// An empty `AtomicLazyCell`.
|
||||
pub const NONE: Self = Self {
|
||||
inner: UnsafeCell::new(None),
|
||||
state: AtomicUsize::new(NONE),
|
||||
};
|
||||
|
||||
/// Creates a new, empty, `AtomicLazyCell`.
|
||||
pub fn new() -> AtomicLazyCell<T> {
|
||||
AtomicLazyCell {
|
||||
inner: UnsafeCell::new(None),
|
||||
state: AtomicUsize::new(NONE),
|
||||
}
|
||||
Self::NONE
|
||||
}
|
||||
|
||||
/// Put a value into this cell.
|
||||
|
@ -187,6 +267,24 @@ impl<T> AtomicLazyCell<T> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Put a value into this cell.
|
||||
///
|
||||
/// Note that this function is infallible but requires `&mut self`. By
|
||||
/// requiring `&mut self` we're guaranteed that no active borrows to this
|
||||
/// cell can exist so we can always fill in the value. This may not always
|
||||
/// be usable, however, as `&mut self` may not be possible to borrow.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// This function returns the previous value, if any.
|
||||
pub fn replace(&mut self, value: T) -> Option<T> {
|
||||
match mem::replace(self.state.get_mut(), SOME) {
|
||||
NONE | SOME => {}
|
||||
_ => panic!("cell in inconsistent state"),
|
||||
}
|
||||
mem::replace(unsafe { &mut *self.inner.get() }, Some(value))
|
||||
}
|
||||
|
||||
/// Test whether this cell has been previously filled.
|
||||
pub fn filled(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire) == SOME
|
||||
|
@ -206,6 +304,10 @@ impl<T> AtomicLazyCell<T> {
|
|||
|
||||
/// Consumes this `LazyCell`, returning the underlying value.
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
// Rust 1.25 changed UnsafeCell::into_inner() from unsafe to safe
|
||||
// function. This unsafe can be removed when supporting Rust older than
|
||||
// 1.25 is not needed.
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { self.inner.into_inner() }
|
||||
}
|
||||
}
|
||||
|
@ -223,6 +325,23 @@ impl<T: Copy> AtomicLazyCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for AtomicLazyCell<T> {
|
||||
/// Create a clone of this `AtomicLazyCell`
|
||||
///
|
||||
/// If self has not been initialized, returns an uninitialized `AtomicLazyCell`
|
||||
/// otherwise returns an `AtomicLazyCell` already initialized with a clone of the
|
||||
/// contents of self.
|
||||
fn clone(&self) -> AtomicLazyCell<T> {
|
||||
self.borrow().map_or(
|
||||
Self::NONE,
|
||||
|v| AtomicLazyCell {
|
||||
inner: UnsafeCell::new(Some(v.clone())),
|
||||
state: AtomicUsize::new(SOME),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Sync + Send> Sync for AtomicLazyCell<T> {}
|
||||
|
||||
unsafe impl<T: Send> Send for AtomicLazyCell<T> {}
|
||||
|
@ -324,6 +443,37 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrow_mut_with() {
|
||||
let mut lazycell = LazyCell::new();
|
||||
|
||||
{
|
||||
let value = lazycell.borrow_mut_with(|| 1);
|
||||
assert_eq!(&mut 1, value);
|
||||
*value = 2;
|
||||
}
|
||||
assert_eq!(&2, lazycell.borrow().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrow_mut_with_already_filled() {
|
||||
let mut lazycell = LazyCell::new();
|
||||
lazycell.fill(1).unwrap();
|
||||
|
||||
let value = lazycell.borrow_mut_with(|| 1);
|
||||
assert_eq!(&1, value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrow_mut_with_not_called_when_filled() {
|
||||
let mut lazycell = LazyCell::new();
|
||||
|
||||
lazycell.fill(1).unwrap();
|
||||
|
||||
let value = lazycell.borrow_mut_with(|| 2);
|
||||
assert_eq!(&1, value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_borrow_with_ok() {
|
||||
let lazycell = LazyCell::new();
|
||||
|
@ -360,6 +510,32 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_borrow_mut_with_ok() {
|
||||
let mut lazycell = LazyCell::new();
|
||||
{
|
||||
let result = lazycell.try_borrow_mut_with::<(), _>(|| Ok(1));
|
||||
assert_eq!(result, Ok(&mut 1));
|
||||
*result.unwrap() = 2;
|
||||
}
|
||||
assert_eq!(&mut 2, lazycell.borrow().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_borrow_mut_with_err() {
|
||||
let mut lazycell = LazyCell::<()>::new();
|
||||
let result = lazycell.try_borrow_mut_with(|| Err(1));
|
||||
assert_eq!(result, Err(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_borrow_mut_with_already_filled() {
|
||||
let mut lazycell = LazyCell::new();
|
||||
lazycell.fill(1).unwrap();
|
||||
let result = lazycell.try_borrow_mut_with::<(), _>(|| unreachable!());
|
||||
assert_eq!(result, Ok(&mut 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let lazycell = LazyCell::new();
|
||||
|
@ -411,4 +587,63 @@ mod tests {
|
|||
let value = lazycell.into_inner();
|
||||
assert_eq!(value, Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_replace() {
|
||||
let mut cell = LazyCell::new();
|
||||
assert_eq!(cell.fill(1), Ok(()));
|
||||
assert_eq!(cell.replace(2), Some(1));
|
||||
assert_eq!(cell.replace(3), Some(2));
|
||||
assert_eq!(cell.borrow(), Some(&3));
|
||||
|
||||
let mut cell = LazyCell::new();
|
||||
assert_eq!(cell.replace(2), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_replace() {
|
||||
let mut cell = AtomicLazyCell::new();
|
||||
assert_eq!(cell.fill(1), Ok(()));
|
||||
assert_eq!(cell.replace(2), Some(1));
|
||||
assert_eq!(cell.replace(3), Some(2));
|
||||
assert_eq!(cell.borrow(), Some(&3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone() {
|
||||
let mut cell = LazyCell::new();
|
||||
let clone1 = cell.clone();
|
||||
assert_eq!(clone1.borrow(), None);
|
||||
assert_eq!(cell.fill(1), Ok(()));
|
||||
let mut clone2 = cell.clone();
|
||||
assert_eq!(clone1.borrow(), None);
|
||||
assert_eq!(clone2.borrow(), Some(&1));
|
||||
assert_eq!(cell.replace(2), Some(1));
|
||||
assert_eq!(clone1.borrow(), None);
|
||||
assert_eq!(clone2.borrow(), Some(&1));
|
||||
assert_eq!(clone1.fill(3), Ok(()));
|
||||
assert_eq!(clone2.replace(4), Some(1));
|
||||
assert_eq!(clone1.borrow(), Some(&3));
|
||||
assert_eq!(clone2.borrow(), Some(&4));
|
||||
assert_eq!(cell.borrow(), Some(&2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_atomic() {
|
||||
let mut cell = AtomicLazyCell::new();
|
||||
let clone1 = cell.clone();
|
||||
assert_eq!(clone1.borrow(), None);
|
||||
assert_eq!(cell.fill(1), Ok(()));
|
||||
let mut clone2 = cell.clone();
|
||||
assert_eq!(clone1.borrow(), None);
|
||||
assert_eq!(clone2.borrow(), Some(&1));
|
||||
assert_eq!(cell.replace(2), Some(1));
|
||||
assert_eq!(clone1.borrow(), None);
|
||||
assert_eq!(clone2.borrow(), Some(&1));
|
||||
assert_eq!(clone1.fill(3), Ok(()));
|
||||
assert_eq!(clone2.replace(4), Some(1));
|
||||
assert_eq!(clone1.borrow(), Some(&3));
|
||||
assert_eq!(clone2.borrow(), Some(&4));
|
||||
assert_eq!(cell.borrow(), Some(&2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"9f7efb5e07d7bba6e6db9142f35d8379144d3707eaf767ca41759bbfa91e0422","Cargo.toml":"37ea791a7fee2939e800737d62c2cd3189e6c71b57935de9ae02519b04d4dc4c","LICENSE-APACHE":"406e5cbaa2ad1178c300cf28ac5258e8d0db0de4f061e78db559d30e6f38e25c","LICENSE-MIT":"8aa414e6c821efd8be6bade07368a5d9f51f5cc55718bc54e10a59eb826b8d58","README.md":"337973d4db2bd42559bded991de98bf3929d39df00d5f17221e4b493075f8317","src/channel.rs":"bfc4f386595291418a5975250f12431d4fe92714cf422369e6a7960754719b51","src/lib.rs":"2ed1572d3255208681d017265df7f642eb4898b1c2dace91676935f55e03eb04","src/timer.rs":"2aa0070b4d59f74262c926d5378781896260c2264d06e47aa2fc1cafb6cf104b","test/mod.rs":"aa3afc2582f00e5e2a2e5b87d12eb9810b0eed3248b48abef7094fd8d02d9c41","test/test_poll_channel.rs":"a96f5749438853cb8c0727e304eabfd7f23400802da73938337ab29210787bcd","test/test_timer.rs":"082c48a320f603d8a99f6ed13c0efe7f48638d33222782e85364575ce805c20c"},"package":"46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"}
|
|
@ -0,0 +1,31 @@
|
|||
## 2.0.5 (18 Jun 2018)
|
||||
|
||||
* update `lazycell` from 0.6 -> 1.0
|
||||
|
||||
## 2.0.4 (7 Apr 2018)
|
||||
|
||||
* Bump mio dependency (fixes minimal-versions build)
|
||||
|
||||
## 2.0.3 (28 Dec 2017)
|
||||
|
||||
* update `log` from 0.3 -> 0.4
|
||||
|
||||
## 2.0.2
|
||||
|
||||
* More docs tidying.
|
||||
|
||||
## 2.0.1
|
||||
|
||||
* Another try at documenting the timer interface.
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Remove channel implementation details from the API. Specifically, the following are no longer public:
|
||||
* `ctl_pair()`
|
||||
* `SenderCtl`
|
||||
* `ReceiverCtl`
|
||||
* Document all APIs
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* Initial release. Essentially identical to [mio-more](https://github.com/carllerche/mio-more).
|
|
@ -0,0 +1,39 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "mio-extras"
|
||||
version = "2.0.5"
|
||||
authors = ["Carl Lerche <me@carllerche.com>", "David Hotham"]
|
||||
exclude = [".gitignore"]
|
||||
description = "Extra components for use with Mio"
|
||||
documentation = "https://docs.rs/mio-extras"
|
||||
readme = "README.md"
|
||||
keywords = ["io", "async", "non-blocking"]
|
||||
categories = ["asynchronous"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/dimbleby/mio-extras"
|
||||
|
||||
[[test]]
|
||||
name = "test"
|
||||
path = "test/mod.rs"
|
||||
[dependencies.lazycell]
|
||||
version = "1"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.mio]
|
||||
version = "0.6.14"
|
||||
|
||||
[dependencies.slab]
|
||||
version = "0.4"
|
|
@ -0,0 +1,201 @@
|
|||
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 2017 Mio authors
|
||||
|
||||
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.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2017 Mio authors
|
||||
|
||||
Permission 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.
|
|
@ -0,0 +1,28 @@
|
|||
# mio-extras
|
||||
|
||||
Extra components for use with [Mio](https://github.com/carllerche/mio):
|
||||
|
||||
* a channel that implements `Evented`
|
||||
* a timer that implements `Evented`
|
||||
|
||||
[![Build Status](https://travis-ci.org/dimbleby/mio-extras.svg?branch=master)](https://travis-ci.org/dimbleby/mio-extras)
|
||||
[![crates.io](http://meritbadge.herokuapp.com/mio-extras)](https://crates.io/crates/mio-extras)
|
||||
|
||||
[Documentation](https://docs.rs/mio-extras).
|
||||
|
||||
|
||||
## History and maintenance
|
||||
|
||||
This repository is forked from [`mio-more`](https://github.com/carllerche/mio-more), which is unmaintained.
|
||||
|
||||
I don't intend to do very much with this except for routine maintenance - bug fixes, updating dependencies, and suchlike.
|
||||
|
||||
However if you have some code that you think belongs here, then by all means raise an issue or open a pull request.
|
||||
|
||||
# License
|
||||
|
||||
`mio-extras` is primarily distributed under the terms of both the MIT license
|
||||
and the Apache License (Version 2.0), with portions covered by various BSD-like
|
||||
licenses.
|
||||
|
||||
See LICENSE-APACHE, and LICENSE-MIT for details.
|
|
@ -0,0 +1,431 @@
|
|||
//! Thread safe communication channel implementing `Evented`
|
||||
use lazycell::{AtomicLazyCell, LazyCell};
|
||||
use mio::{Evented, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
|
||||
use std::any::Any;
|
||||
use std::error;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::{fmt, io};
|
||||
|
||||
/// Creates a new asynchronous channel, where the `Receiver` can be registered
|
||||
/// with `Poll`.
|
||||
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
let (tx_ctl, rx_ctl) = ctl_pair();
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let tx = Sender { tx, ctl: tx_ctl };
|
||||
|
||||
let rx = Receiver { rx, ctl: rx_ctl };
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
/// Creates a new synchronous, bounded channel where the `Receiver` can be
|
||||
/// registered with `Poll`.
|
||||
pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Receiver<T>) {
|
||||
let (tx_ctl, rx_ctl) = ctl_pair();
|
||||
let (tx, rx) = mpsc::sync_channel(bound);
|
||||
|
||||
let tx = SyncSender { tx, ctl: tx_ctl };
|
||||
|
||||
let rx = Receiver { rx, ctl: rx_ctl };
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
fn ctl_pair() -> (SenderCtl, ReceiverCtl) {
|
||||
let inner = Arc::new(Inner {
|
||||
pending: AtomicUsize::new(0),
|
||||
senders: AtomicUsize::new(1),
|
||||
set_readiness: AtomicLazyCell::new(),
|
||||
});
|
||||
|
||||
let tx = SenderCtl {
|
||||
inner: Arc::clone(&inner),
|
||||
};
|
||||
|
||||
let rx = ReceiverCtl {
|
||||
registration: LazyCell::new(),
|
||||
inner,
|
||||
};
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
/// Tracks messages sent on a channel in order to update readiness.
|
||||
struct SenderCtl {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
/// Tracks messages received on a channel in order to track readiness.
|
||||
struct ReceiverCtl {
|
||||
registration: LazyCell<Registration>,
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
/// The sending half of a channel.
|
||||
pub struct Sender<T> {
|
||||
tx: mpsc::Sender<T>,
|
||||
ctl: SenderCtl,
|
||||
}
|
||||
|
||||
/// The sending half of a synchronous channel.
|
||||
pub struct SyncSender<T> {
|
||||
tx: mpsc::SyncSender<T>,
|
||||
ctl: SenderCtl,
|
||||
}
|
||||
|
||||
/// The receiving half of a channel.
|
||||
pub struct Receiver<T> {
|
||||
rx: mpsc::Receiver<T>,
|
||||
ctl: ReceiverCtl,
|
||||
}
|
||||
|
||||
/// An error returned from the `Sender::send` or `SyncSender::send` function.
|
||||
pub enum SendError<T> {
|
||||
/// An IO error.
|
||||
Io(io::Error),
|
||||
|
||||
/// The receiving half of the channel has disconnected.
|
||||
Disconnected(T),
|
||||
}
|
||||
|
||||
/// An error returned from the `SyncSender::try_send` function.
|
||||
pub enum TrySendError<T> {
|
||||
/// An IO error.
|
||||
Io(io::Error),
|
||||
|
||||
/// Data could not be sent because it would require the callee to block.
|
||||
Full(T),
|
||||
|
||||
/// The receiving half of the channel has disconnected.
|
||||
Disconnected(T),
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
// The number of outstanding messages for the receiver to read
|
||||
pending: AtomicUsize,
|
||||
// The number of sender handles
|
||||
senders: AtomicUsize,
|
||||
// The set readiness handle
|
||||
set_readiness: AtomicLazyCell<SetReadiness>,
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
/// Attempts to send a value on this channel, returning it back if it could not be sent.
|
||||
pub fn send(&self, t: T) -> Result<(), SendError<T>> {
|
||||
self.tx.send(t).map_err(SendError::from).and_then(|_| {
|
||||
try!(self.ctl.inc());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
fn clone(&self) -> Sender<T> {
|
||||
Sender {
|
||||
tx: self.tx.clone(),
|
||||
ctl: self.ctl.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SyncSender<T> {
|
||||
/// Sends a value on this synchronous channel.
|
||||
///
|
||||
/// This function will *block* until space in the internal buffer becomes
|
||||
/// available or a receiver is available to hand off the message to.
|
||||
pub fn send(&self, t: T) -> Result<(), SendError<T>> {
|
||||
self.tx.send(t).map_err(From::from).and_then(|_| {
|
||||
try!(self.ctl.inc());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to send a value on this channel without blocking.
|
||||
///
|
||||
/// This method differs from `send` by returning immediately if the channel's
|
||||
/// buffer is full or no receiver is waiting to acquire some data.
|
||||
pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> {
|
||||
self.tx.try_send(t).map_err(From::from).and_then(|_| {
|
||||
try!(self.ctl.inc());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for SyncSender<T> {
|
||||
fn clone(&self) -> SyncSender<T> {
|
||||
SyncSender {
|
||||
tx: self.tx.clone(),
|
||||
ctl: self.ctl.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Receiver<T> {
|
||||
/// Attempts to return a pending value on this receiver without blocking.
|
||||
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
|
||||
self.rx.try_recv().and_then(|res| {
|
||||
let _ = self.ctl.dec();
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Evented for Receiver<T> {
|
||||
fn register(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
self.ctl.register(poll, token, interest, opts)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
self.ctl.reregister(poll, token, interest, opts)
|
||||
}
|
||||
|
||||
fn deregister(&self, poll: &Poll) -> io::Result<()> {
|
||||
self.ctl.deregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== SenderCtl / ReceiverCtl =====
|
||||
*
|
||||
*/
|
||||
|
||||
impl SenderCtl {
|
||||
/// Call to track that a message has been sent
|
||||
fn inc(&self) -> io::Result<()> {
|
||||
let cnt = self.inner.pending.fetch_add(1, Ordering::Acquire);
|
||||
|
||||
if 0 == cnt {
|
||||
// Toggle readiness to readable
|
||||
if let Some(set_readiness) = self.inner.set_readiness.borrow() {
|
||||
try!(set_readiness.set_readiness(Ready::readable()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SenderCtl {
|
||||
fn clone(&self) -> SenderCtl {
|
||||
self.inner.senders.fetch_add(1, Ordering::Relaxed);
|
||||
SenderCtl {
|
||||
inner: Arc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SenderCtl {
|
||||
fn drop(&mut self) {
|
||||
if self.inner.senders.fetch_sub(1, Ordering::Release) == 1 {
|
||||
let _ = self.inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceiverCtl {
|
||||
fn dec(&self) -> io::Result<()> {
|
||||
let first = self.inner.pending.load(Ordering::Acquire);
|
||||
|
||||
if first == 1 {
|
||||
// Unset readiness
|
||||
if let Some(set_readiness) = self.inner.set_readiness.borrow() {
|
||||
try!(set_readiness.set_readiness(Ready::empty()));
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement
|
||||
let second = self.inner.pending.fetch_sub(1, Ordering::AcqRel);
|
||||
|
||||
if first == 1 && second > 1 {
|
||||
// There are still pending messages. Since readiness was
|
||||
// previously unset, it must be reset here
|
||||
if let Some(set_readiness) = self.inner.set_readiness.borrow() {
|
||||
try!(set_readiness.set_readiness(Ready::readable()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Evented for ReceiverCtl {
|
||||
fn register(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
if self.registration.borrow().is_some() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"receiver already registered",
|
||||
));
|
||||
}
|
||||
|
||||
let (registration, set_readiness) = Registration::new2();
|
||||
poll.register(®istration, token, interest, opts)?;
|
||||
|
||||
if self.inner.pending.load(Ordering::Relaxed) > 0 {
|
||||
// TODO: Don't drop readiness
|
||||
let _ = set_readiness.set_readiness(Ready::readable());
|
||||
}
|
||||
|
||||
self.registration
|
||||
.fill(registration)
|
||||
.expect("unexpected state encountered");
|
||||
self.inner
|
||||
.set_readiness
|
||||
.fill(set_readiness)
|
||||
.expect("unexpected state encountered");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
match self.registration.borrow() {
|
||||
Some(registration) => poll.reregister(registration, token, interest, opts),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"receiver not registered",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deregister(&self, poll: &Poll) -> io::Result<()> {
|
||||
match self.registration.borrow() {
|
||||
Some(registration) => poll.deregister(registration),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"receiver not registered",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== Error conversions =====
|
||||
*
|
||||
*/
|
||||
|
||||
impl<T> From<mpsc::SendError<T>> for SendError<T> {
|
||||
fn from(src: mpsc::SendError<T>) -> SendError<T> {
|
||||
SendError::Disconnected(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<io::Error> for SendError<T> {
|
||||
fn from(src: io::Error) -> SendError<T> {
|
||||
SendError::Io(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<mpsc::TrySendError<T>> for TrySendError<T> {
|
||||
fn from(src: mpsc::TrySendError<T>) -> TrySendError<T> {
|
||||
match src {
|
||||
mpsc::TrySendError::Full(v) => TrySendError::Full(v),
|
||||
mpsc::TrySendError::Disconnected(v) => TrySendError::Disconnected(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<mpsc::SendError<T>> for TrySendError<T> {
|
||||
fn from(src: mpsc::SendError<T>) -> TrySendError<T> {
|
||||
TrySendError::Disconnected(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<io::Error> for TrySendError<T> {
|
||||
fn from(src: io::Error) -> TrySendError<T> {
|
||||
TrySendError::Io(src)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== Implement Error, Debug and Display for Errors =====
|
||||
*
|
||||
*/
|
||||
|
||||
impl<T: Any> error::Error for SendError<T> {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
SendError::Io(ref io_err) => io_err.description(),
|
||||
SendError::Disconnected(..) => "Disconnected",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Any> error::Error for TrySendError<T> {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
TrySendError::Io(ref io_err) => io_err.description(),
|
||||
TrySendError::Full(..) => "Full",
|
||||
TrySendError::Disconnected(..) => "Disconnected",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for SendError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
format_send_error(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for SendError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
format_send_error(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for TrySendError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
format_try_send_error(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for TrySendError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
format_try_send_error(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn format_send_error<T>(e: &SendError<T>, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *e {
|
||||
SendError::Io(ref io_err) => write!(f, "{}", io_err),
|
||||
SendError::Disconnected(..) => write!(f, "Disconnected"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn format_try_send_error<T>(e: &TrySendError<T>, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *e {
|
||||
TrySendError::Io(ref io_err) => write!(f, "{}", io_err),
|
||||
TrySendError::Full(..) => write!(f, "Full"),
|
||||
TrySendError::Disconnected(..) => write!(f, "Disconnected"),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//! Extra components for use with Mio.
|
||||
#![deny(missing_docs)]
|
||||
extern crate lazycell;
|
||||
extern crate mio;
|
||||
extern crate slab;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod channel;
|
||||
pub mod timer;
|
||||
|
||||
// Conversion utilities
|
||||
mod convert {
|
||||
use std::time::Duration;
|
||||
|
||||
const NANOS_PER_MILLI: u32 = 1_000_000;
|
||||
const MILLIS_PER_SEC: u64 = 1_000;
|
||||
|
||||
/// Convert a `Duration` to milliseconds, rounding up and saturating at
|
||||
/// `u64::MAX`.
|
||||
///
|
||||
/// The saturating is fine because `u64::MAX` milliseconds are still many
|
||||
/// million years.
|
||||
pub fn millis(duration: Duration) -> u64 {
|
||||
// Round up.
|
||||
let millis = (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI;
|
||||
duration
|
||||
.as_secs()
|
||||
.saturating_mul(MILLIS_PER_SEC)
|
||||
.saturating_add(u64::from(millis))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,751 @@
|
|||
//! Timer optimized for I/O related operations
|
||||
use convert;
|
||||
use lazycell::LazyCell;
|
||||
use mio::{Evented, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
|
||||
use slab::Slab;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{cmp, fmt, io, iter, thread, u64, usize};
|
||||
|
||||
/// A timer.
|
||||
///
|
||||
/// Typical usage goes like this:
|
||||
///
|
||||
/// * register the timer with a `mio::Poll`.
|
||||
/// * set a timeout, by calling `Timer::set_timeout`. Here you provide some
|
||||
/// state to be associated with this timeout.
|
||||
/// * poll the `Poll`, to learn when a timeout has occurred.
|
||||
/// * retrieve state associated with the timeout by calling `Timer::poll`.
|
||||
///
|
||||
/// You can omit use of the `Poll` altogether, if you like, and just poll the
|
||||
/// `Timer` directly.
|
||||
pub struct Timer<T> {
|
||||
// Size of each tick in milliseconds
|
||||
tick_ms: u64,
|
||||
// Slab of timeout entries
|
||||
entries: Slab<Entry<T>>,
|
||||
// Timeout wheel. Each tick, the timer will look at the next slot for
|
||||
// timeouts that match the current tick.
|
||||
wheel: Vec<WheelEntry>,
|
||||
// Tick 0's time instant
|
||||
start: Instant,
|
||||
// The current tick
|
||||
tick: Tick,
|
||||
// The next entry to possibly timeout
|
||||
next: Token,
|
||||
// Masks the target tick to get the slot
|
||||
mask: u64,
|
||||
// Set on registration with Poll
|
||||
inner: LazyCell<Inner>,
|
||||
}
|
||||
|
||||
/// Used to create a `Timer`.
|
||||
pub struct Builder {
|
||||
// Approximate duration of each tick
|
||||
tick: Duration,
|
||||
// Number of slots in the timer wheel
|
||||
num_slots: usize,
|
||||
// Max number of timeouts that can be in flight at a given time.
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
/// A timeout, as returned by `Timer::set_timeout`.
|
||||
///
|
||||
/// Use this as the argument to `Timer::cancel_timeout`, to cancel this timeout.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Timeout {
|
||||
// Reference into the timer entry slab
|
||||
token: Token,
|
||||
// Tick that it should match up with
|
||||
tick: u64,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
registration: Registration,
|
||||
set_readiness: SetReadiness,
|
||||
wakeup_state: WakeupState,
|
||||
wakeup_thread: thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
// 1. Set wakeup state to TERMINATE_THREAD
|
||||
self.wakeup_state.store(TERMINATE_THREAD, Ordering::Release);
|
||||
// 2. Wake him up
|
||||
self.wakeup_thread.thread().unpark();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct WheelEntry {
|
||||
next_tick: Tick,
|
||||
head: Token,
|
||||
}
|
||||
|
||||
// Doubly linked list of timer entries. Allows for efficient insertion /
|
||||
// removal of timeouts.
|
||||
struct Entry<T> {
|
||||
state: T,
|
||||
links: EntryLinks,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EntryLinks {
|
||||
tick: Tick,
|
||||
prev: Token,
|
||||
next: Token,
|
||||
}
|
||||
|
||||
type Tick = u64;
|
||||
|
||||
const TICK_MAX: Tick = u64::MAX;
|
||||
|
||||
// Manages communication with wakeup thread
|
||||
type WakeupState = Arc<AtomicUsize>;
|
||||
|
||||
const TERMINATE_THREAD: usize = 0;
|
||||
const EMPTY: Token = Token(usize::MAX);
|
||||
|
||||
impl Builder {
|
||||
/// Set the tick duration. Default is 100ms.
|
||||
pub fn tick_duration(mut self, duration: Duration) -> Builder {
|
||||
self.tick = duration;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the number of slots. Default is 256.
|
||||
pub fn num_slots(mut self, num_slots: usize) -> Builder {
|
||||
self.num_slots = num_slots;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the capacity. Default is 65536.
|
||||
pub fn capacity(mut self, capacity: usize) -> Builder {
|
||||
self.capacity = capacity;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a `Timer` with the parameters set on this `Builder`.
|
||||
pub fn build<T>(self) -> Timer<T> {
|
||||
Timer::new(
|
||||
convert::millis(self.tick),
|
||||
self.num_slots,
|
||||
self.capacity,
|
||||
Instant::now(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Builder {
|
||||
Builder {
|
||||
tick: Duration::from_millis(100),
|
||||
num_slots: 1 << 8,
|
||||
capacity: 1 << 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Timer<T> {
|
||||
fn new(tick_ms: u64, num_slots: usize, capacity: usize, start: Instant) -> Timer<T> {
|
||||
let num_slots = num_slots.next_power_of_two();
|
||||
let capacity = capacity.next_power_of_two();
|
||||
let mask = (num_slots as u64) - 1;
|
||||
let wheel = iter::repeat(WheelEntry {
|
||||
next_tick: TICK_MAX,
|
||||
head: EMPTY,
|
||||
}).take(num_slots)
|
||||
.collect();
|
||||
|
||||
Timer {
|
||||
tick_ms,
|
||||
entries: Slab::with_capacity(capacity),
|
||||
wheel,
|
||||
start,
|
||||
tick: 0,
|
||||
next: EMPTY,
|
||||
mask,
|
||||
inner: LazyCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a timeout.
|
||||
///
|
||||
/// When the timeout occurs, the given state becomes available via `poll`.
|
||||
pub fn set_timeout(&mut self, delay_from_now: Duration, state: T) -> Timeout {
|
||||
let delay_from_start = self.start.elapsed() + delay_from_now;
|
||||
self.set_timeout_at(delay_from_start, state)
|
||||
}
|
||||
|
||||
fn set_timeout_at(&mut self, delay_from_start: Duration, state: T) -> Timeout {
|
||||
let mut tick = duration_to_tick(delay_from_start, self.tick_ms);
|
||||
trace!(
|
||||
"setting timeout; delay={:?}; tick={:?}; current-tick={:?}",
|
||||
delay_from_start,
|
||||
tick,
|
||||
self.tick
|
||||
);
|
||||
|
||||
// Always target at least 1 tick in the future
|
||||
if tick <= self.tick {
|
||||
tick = self.tick + 1;
|
||||
}
|
||||
|
||||
self.insert(tick, state)
|
||||
}
|
||||
|
||||
fn insert(&mut self, tick: Tick, state: T) -> Timeout {
|
||||
// Get the slot for the requested tick
|
||||
let slot = (tick & self.mask) as usize;
|
||||
let curr = self.wheel[slot];
|
||||
|
||||
// Insert the new entry
|
||||
let entry = Entry::new(state, tick, curr.head);
|
||||
let token = Token(self.entries.insert(entry));
|
||||
|
||||
if curr.head != EMPTY {
|
||||
// If there was a previous entry, set its prev pointer to the new
|
||||
// entry
|
||||
self.entries[curr.head.into()].links.prev = token;
|
||||
}
|
||||
|
||||
// Update the head slot
|
||||
self.wheel[slot] = WheelEntry {
|
||||
next_tick: cmp::min(tick, curr.next_tick),
|
||||
head: token,
|
||||
};
|
||||
|
||||
self.schedule_readiness(tick);
|
||||
|
||||
trace!("inserted timout; slot={}; token={:?}", slot, token);
|
||||
|
||||
// Return the new timeout
|
||||
Timeout { token, tick }
|
||||
}
|
||||
|
||||
/// Cancel a timeout.
|
||||
///
|
||||
/// If the timeout has not yet occurred, the return value holds the
|
||||
/// associated state.
|
||||
pub fn cancel_timeout(&mut self, timeout: &Timeout) -> Option<T> {
|
||||
let links = match self.entries.get(timeout.token.into()) {
|
||||
Some(e) => e.links,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// Sanity check
|
||||
if links.tick != timeout.tick {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.unlink(&links, timeout.token);
|
||||
Some(self.entries.remove(timeout.token.into()).state)
|
||||
}
|
||||
|
||||
/// Poll for an expired timer.
|
||||
///
|
||||
/// The return value holds the state associated with the first expired
|
||||
/// timer, if any.
|
||||
pub fn poll(&mut self) -> Option<T> {
|
||||
let target_tick = current_tick(self.start, self.tick_ms);
|
||||
self.poll_to(target_tick)
|
||||
}
|
||||
|
||||
fn poll_to(&mut self, mut target_tick: Tick) -> Option<T> {
|
||||
trace!(
|
||||
"tick_to; target_tick={}; current_tick={}",
|
||||
target_tick,
|
||||
self.tick
|
||||
);
|
||||
|
||||
if target_tick < self.tick {
|
||||
target_tick = self.tick;
|
||||
}
|
||||
|
||||
while self.tick <= target_tick {
|
||||
let curr = self.next;
|
||||
|
||||
trace!("ticking; curr={:?}", curr);
|
||||
|
||||
if curr == EMPTY {
|
||||
self.tick += 1;
|
||||
|
||||
let slot = self.slot_for(self.tick);
|
||||
self.next = self.wheel[slot].head;
|
||||
|
||||
// Handle the case when a slot has a single timeout which gets
|
||||
// canceled before the timeout expires. In this case, the
|
||||
// slot's head is EMPTY but there is a value for next_tick. Not
|
||||
// resetting next_tick here causes the timer to get stuck in a
|
||||
// loop.
|
||||
if self.next == EMPTY {
|
||||
self.wheel[slot].next_tick = TICK_MAX;
|
||||
}
|
||||
} else {
|
||||
let slot = self.slot_for(self.tick);
|
||||
|
||||
if curr == self.wheel[slot].head {
|
||||
self.wheel[slot].next_tick = TICK_MAX;
|
||||
}
|
||||
|
||||
let links = self.entries[curr.into()].links;
|
||||
|
||||
if links.tick <= self.tick {
|
||||
trace!("triggering; token={:?}", curr);
|
||||
|
||||
// Unlink will also advance self.next
|
||||
self.unlink(&links, curr);
|
||||
|
||||
// Remove and return the token
|
||||
return Some(self.entries.remove(curr.into()).state);
|
||||
} else {
|
||||
let next_tick = self.wheel[slot].next_tick;
|
||||
self.wheel[slot].next_tick = cmp::min(next_tick, links.tick);
|
||||
self.next = links.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No more timeouts to poll
|
||||
if let Some(inner) = self.inner.borrow() {
|
||||
trace!("unsetting readiness");
|
||||
let _ = inner.set_readiness.set_readiness(Ready::empty());
|
||||
|
||||
if let Some(tick) = self.next_tick() {
|
||||
self.schedule_readiness(tick);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn unlink(&mut self, links: &EntryLinks, token: Token) {
|
||||
trace!(
|
||||
"unlinking timeout; slot={}; token={:?}",
|
||||
self.slot_for(links.tick),
|
||||
token
|
||||
);
|
||||
|
||||
if links.prev == EMPTY {
|
||||
let slot = self.slot_for(links.tick);
|
||||
self.wheel[slot].head = links.next;
|
||||
} else {
|
||||
self.entries[links.prev.into()].links.next = links.next;
|
||||
}
|
||||
|
||||
if links.next != EMPTY {
|
||||
self.entries[links.next.into()].links.prev = links.prev;
|
||||
|
||||
if token == self.next {
|
||||
self.next = links.next;
|
||||
}
|
||||
} else if token == self.next {
|
||||
self.next = EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_readiness(&self, tick: Tick) {
|
||||
if let Some(inner) = self.inner.borrow() {
|
||||
// Coordinate setting readiness w/ the wakeup thread
|
||||
let mut curr = inner.wakeup_state.load(Ordering::Acquire);
|
||||
|
||||
loop {
|
||||
if curr as Tick <= tick {
|
||||
// Nothing to do, wakeup is already scheduled
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to move the wakeup time forward
|
||||
trace!("advancing the wakeup time; target={}; curr={}", tick, curr);
|
||||
let actual =
|
||||
inner
|
||||
.wakeup_state
|
||||
.compare_and_swap(curr, tick as usize, Ordering::Release);
|
||||
|
||||
if actual == curr {
|
||||
// Signal to the wakeup thread that the wakeup time has
|
||||
// been changed.
|
||||
trace!("unparking wakeup thread");
|
||||
inner.wakeup_thread.thread().unpark();
|
||||
return;
|
||||
}
|
||||
|
||||
curr = actual;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next tick containing a timeout
|
||||
fn next_tick(&self) -> Option<Tick> {
|
||||
if self.next != EMPTY {
|
||||
let slot = self.slot_for(self.entries[self.next.into()].links.tick);
|
||||
|
||||
if self.wheel[slot].next_tick == self.tick {
|
||||
// There is data ready right now
|
||||
return Some(self.tick);
|
||||
}
|
||||
}
|
||||
|
||||
self.wheel.iter().map(|e| e.next_tick).min()
|
||||
}
|
||||
|
||||
fn slot_for(&self, tick: Tick) -> usize {
|
||||
(self.mask & tick) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Timer<T> {
|
||||
fn default() -> Timer<T> {
|
||||
Builder::default().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Evented for Timer<T> {
|
||||
fn register(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
if self.inner.borrow().is_some() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"timer already registered",
|
||||
));
|
||||
}
|
||||
|
||||
let (registration, set_readiness) = Registration::new2();
|
||||
poll.register(®istration, token, interest, opts)?;
|
||||
let wakeup_state = Arc::new(AtomicUsize::new(usize::MAX));
|
||||
let thread_handle = spawn_wakeup_thread(
|
||||
Arc::clone(&wakeup_state),
|
||||
set_readiness.clone(),
|
||||
self.start,
|
||||
self.tick_ms,
|
||||
);
|
||||
|
||||
self.inner
|
||||
.fill(Inner {
|
||||
registration,
|
||||
set_readiness,
|
||||
wakeup_state,
|
||||
wakeup_thread: thread_handle,
|
||||
})
|
||||
.expect("timer already registered");
|
||||
|
||||
if let Some(next_tick) = self.next_tick() {
|
||||
self.schedule_readiness(next_tick);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&self,
|
||||
poll: &Poll,
|
||||
token: Token,
|
||||
interest: Ready,
|
||||
opts: PollOpt,
|
||||
) -> io::Result<()> {
|
||||
match self.inner.borrow() {
|
||||
Some(inner) => poll.reregister(&inner.registration, token, interest, opts),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"receiver not registered",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deregister(&self, poll: &Poll) -> io::Result<()> {
|
||||
match self.inner.borrow() {
|
||||
Some(inner) => poll.deregister(&inner.registration),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"receiver not registered",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Inner {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Inner")
|
||||
.field("registration", &self.registration)
|
||||
.field("wakeup_state", &self.wakeup_state.load(Ordering::Relaxed))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_wakeup_thread(
|
||||
state: WakeupState,
|
||||
set_readiness: SetReadiness,
|
||||
start: Instant,
|
||||
tick_ms: u64,
|
||||
) -> thread::JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
let mut sleep_until_tick = state.load(Ordering::Acquire) as Tick;
|
||||
|
||||
loop {
|
||||
if sleep_until_tick == TERMINATE_THREAD as Tick {
|
||||
return;
|
||||
}
|
||||
|
||||
let now_tick = current_tick(start, tick_ms);
|
||||
|
||||
trace!(
|
||||
"wakeup thread: sleep_until_tick={:?}; now_tick={:?}",
|
||||
sleep_until_tick,
|
||||
now_tick
|
||||
);
|
||||
|
||||
if now_tick < sleep_until_tick {
|
||||
// Calling park_timeout with u64::MAX leads to undefined
|
||||
// behavior in pthread, causing the park to return immediately
|
||||
// and causing the thread to tightly spin. Instead of u64::MAX
|
||||
// on large values, simply use a blocking park.
|
||||
match tick_ms.checked_mul(sleep_until_tick - now_tick) {
|
||||
Some(sleep_duration) => {
|
||||
trace!(
|
||||
"sleeping; tick_ms={}; now_tick={}; sleep_until_tick={}; duration={:?}",
|
||||
tick_ms,
|
||||
now_tick,
|
||||
sleep_until_tick,
|
||||
sleep_duration
|
||||
);
|
||||
thread::park_timeout(Duration::from_millis(sleep_duration));
|
||||
}
|
||||
None => {
|
||||
trace!(
|
||||
"sleeping; tick_ms={}; now_tick={}; blocking sleep",
|
||||
tick_ms,
|
||||
now_tick
|
||||
);
|
||||
thread::park();
|
||||
}
|
||||
}
|
||||
sleep_until_tick = state.load(Ordering::Acquire) as Tick;
|
||||
} else {
|
||||
let actual =
|
||||
state.compare_and_swap(sleep_until_tick as usize, usize::MAX, Ordering::AcqRel)
|
||||
as Tick;
|
||||
|
||||
if actual == sleep_until_tick {
|
||||
trace!("setting readiness from wakeup thread");
|
||||
let _ = set_readiness.set_readiness(Ready::readable());
|
||||
sleep_until_tick = usize::MAX as Tick;
|
||||
} else {
|
||||
sleep_until_tick = actual as Tick;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn duration_to_tick(elapsed: Duration, tick_ms: u64) -> Tick {
|
||||
// Calculate tick rounding up to the closest one
|
||||
let elapsed_ms = convert::millis(elapsed);
|
||||
elapsed_ms.saturating_add(tick_ms / 2) / tick_ms
|
||||
}
|
||||
|
||||
fn current_tick(start: Instant, tick_ms: u64) -> Tick {
|
||||
duration_to_tick(start.elapsed(), tick_ms)
|
||||
}
|
||||
|
||||
impl<T> Entry<T> {
|
||||
fn new(state: T, tick: u64, next: Token) -> Entry<T> {
|
||||
Entry {
|
||||
state,
|
||||
links: EntryLinks {
|
||||
tick,
|
||||
prev: EMPTY,
|
||||
next,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
pub fn test_timeout_next_tick() {
|
||||
let mut t = timer();
|
||||
let mut tick;
|
||||
|
||||
t.set_timeout_at(Duration::from_millis(100), "a");
|
||||
|
||||
tick = ms_to_tick(&t, 50);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 100);
|
||||
assert_eq!(Some("a"), t.poll_to(tick));
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 150);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 200);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
assert_eq!(count(&t), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_clearing_timeout() {
|
||||
let mut t = timer();
|
||||
let mut tick;
|
||||
|
||||
let to = t.set_timeout_at(Duration::from_millis(100), "a");
|
||||
assert_eq!("a", t.cancel_timeout(&to).unwrap());
|
||||
|
||||
tick = ms_to_tick(&t, 100);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 200);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
assert_eq!(count(&t), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_multiple_timeouts_same_tick() {
|
||||
let mut t = timer();
|
||||
let mut tick;
|
||||
|
||||
t.set_timeout_at(Duration::from_millis(100), "a");
|
||||
t.set_timeout_at(Duration::from_millis(100), "b");
|
||||
|
||||
let mut rcv = vec![];
|
||||
|
||||
tick = ms_to_tick(&t, 100);
|
||||
rcv.push(t.poll_to(tick).unwrap());
|
||||
rcv.push(t.poll_to(tick).unwrap());
|
||||
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
rcv.sort();
|
||||
assert!(rcv == ["a", "b"], "actual={:?}", rcv);
|
||||
|
||||
tick = ms_to_tick(&t, 200);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
assert_eq!(count(&t), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_multiple_timeouts_diff_tick() {
|
||||
let mut t = timer();
|
||||
let mut tick;
|
||||
|
||||
t.set_timeout_at(Duration::from_millis(110), "a");
|
||||
t.set_timeout_at(Duration::from_millis(220), "b");
|
||||
t.set_timeout_at(Duration::from_millis(230), "c");
|
||||
t.set_timeout_at(Duration::from_millis(440), "d");
|
||||
t.set_timeout_at(Duration::from_millis(560), "e");
|
||||
|
||||
tick = ms_to_tick(&t, 100);
|
||||
assert_eq!(Some("a"), t.poll_to(tick));
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 200);
|
||||
assert_eq!(Some("c"), t.poll_to(tick));
|
||||
assert_eq!(Some("b"), t.poll_to(tick));
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 300);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 400);
|
||||
assert_eq!(Some("d"), t.poll_to(tick));
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 500);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 600);
|
||||
assert_eq!(Some("e"), t.poll_to(tick));
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_catching_up() {
|
||||
let mut t = timer();
|
||||
|
||||
t.set_timeout_at(Duration::from_millis(110), "a");
|
||||
t.set_timeout_at(Duration::from_millis(220), "b");
|
||||
t.set_timeout_at(Duration::from_millis(230), "c");
|
||||
t.set_timeout_at(Duration::from_millis(440), "d");
|
||||
|
||||
let tick = ms_to_tick(&t, 600);
|
||||
assert_eq!(Some("a"), t.poll_to(tick));
|
||||
assert_eq!(Some("c"), t.poll_to(tick));
|
||||
assert_eq!(Some("b"), t.poll_to(tick));
|
||||
assert_eq!(Some("d"), t.poll_to(tick));
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timeout_hash_collision() {
|
||||
let mut t = timer();
|
||||
let mut tick;
|
||||
|
||||
t.set_timeout_at(Duration::from_millis(100), "a");
|
||||
t.set_timeout_at(Duration::from_millis(100 + TICK * SLOTS as u64), "b");
|
||||
|
||||
tick = ms_to_tick(&t, 100);
|
||||
assert_eq!(Some("a"), t.poll_to(tick));
|
||||
assert_eq!(1, count(&t));
|
||||
|
||||
tick = ms_to_tick(&t, 200);
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
assert_eq!(1, count(&t));
|
||||
|
||||
tick = ms_to_tick(&t, 100 + TICK * SLOTS as u64);
|
||||
assert_eq!(Some("b"), t.poll_to(tick));
|
||||
assert_eq!(0, count(&t));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_clearing_timeout_between_triggers() {
|
||||
let mut t = timer();
|
||||
let mut tick;
|
||||
|
||||
let a = t.set_timeout_at(Duration::from_millis(100), "a");
|
||||
let _ = t.set_timeout_at(Duration::from_millis(100), "b");
|
||||
let _ = t.set_timeout_at(Duration::from_millis(200), "c");
|
||||
|
||||
tick = ms_to_tick(&t, 100);
|
||||
assert_eq!(Some("b"), t.poll_to(tick));
|
||||
assert_eq!(2, count(&t));
|
||||
|
||||
t.cancel_timeout(&a);
|
||||
assert_eq!(1, count(&t));
|
||||
|
||||
assert_eq!(None, t.poll_to(tick));
|
||||
|
||||
tick = ms_to_tick(&t, 200);
|
||||
assert_eq!(Some("c"), t.poll_to(tick));
|
||||
assert_eq!(0, count(&t));
|
||||
}
|
||||
|
||||
const TICK: u64 = 100;
|
||||
const SLOTS: usize = 16;
|
||||
const CAPACITY: usize = 32;
|
||||
|
||||
fn count<T>(timer: &Timer<T>) -> usize {
|
||||
timer.entries.len()
|
||||
}
|
||||
|
||||
fn timer() -> Timer<&'static str> {
|
||||
Timer::new(TICK, SLOTS, CAPACITY, Instant::now())
|
||||
}
|
||||
|
||||
fn ms_to_tick<T>(timer: &Timer<T>, ms: u64) -> u64 {
|
||||
ms / timer.tick_ms
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
extern crate mio;
|
||||
extern crate mio_extras;
|
||||
|
||||
use mio::event::Event;
|
||||
use mio::{Events, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
mod test_poll_channel;
|
||||
mod test_timer;
|
||||
|
||||
pub fn expect_events(
|
||||
poll: &Poll,
|
||||
event_buffer: &mut Events,
|
||||
poll_try_count: usize,
|
||||
mut expected: Vec<Event>,
|
||||
) {
|
||||
const MS: u64 = 1_000;
|
||||
|
||||
for _ in 0..poll_try_count {
|
||||
poll.poll(event_buffer, Some(Duration::from_millis(MS)))
|
||||
.unwrap();
|
||||
for event in event_buffer.iter() {
|
||||
let pos_opt = match expected.iter().position(|exp_event| {
|
||||
(event.token() == exp_event.token())
|
||||
&& event.readiness().contains(exp_event.readiness())
|
||||
}) {
|
||||
Some(x) => Some(x),
|
||||
None => None,
|
||||
};
|
||||
if let Some(pos) = pos_opt {
|
||||
expected.remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
if expected.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
expected.is_empty(),
|
||||
"The following expected events were not found: {:?}",
|
||||
expected
|
||||
);
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
use expect_events;
|
||||
use mio::event::Event;
|
||||
use mio::{Events, Poll, PollOpt, Ready, Token};
|
||||
use mio_extras::channel;
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
pub fn test_poll_channel_edge() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let (tx, rx) = channel::channel();
|
||||
|
||||
poll.register(&rx, Token(123), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Push the value
|
||||
tx.send("hello").unwrap();
|
||||
|
||||
// Polling will contain the event
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(1, num);
|
||||
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(event.token(), Token(123));
|
||||
assert_eq!(event.readiness(), Ready::readable());
|
||||
|
||||
// Poll again and there should be no events
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Read the value
|
||||
assert_eq!("hello", rx.try_recv().unwrap());
|
||||
|
||||
// Poll again, nothing
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Push a value
|
||||
tx.send("goodbye").unwrap();
|
||||
|
||||
// Have an event
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(1, num);
|
||||
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(event.token(), Token(123));
|
||||
assert_eq!(event.readiness(), Ready::readable());
|
||||
|
||||
// Read the value
|
||||
rx.try_recv().unwrap();
|
||||
|
||||
// Drop the sender half
|
||||
drop(tx);
|
||||
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(1, num);
|
||||
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(event.token(), Token(123));
|
||||
assert_eq!(event.readiness(), Ready::readable());
|
||||
|
||||
match rx.try_recv() {
|
||||
Err(TryRecvError::Disconnected) => {}
|
||||
no => panic!("unexpected value {:?}", no),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_poll_channel_oneshot() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let (tx, rx) = channel::channel();
|
||||
|
||||
poll.register(
|
||||
&rx,
|
||||
Token(123),
|
||||
Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot(),
|
||||
).unwrap();
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Push the value
|
||||
tx.send("hello").unwrap();
|
||||
|
||||
// Polling will contain the event
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(1, num);
|
||||
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(event.token(), Token(123));
|
||||
assert_eq!(event.readiness(), Ready::readable());
|
||||
|
||||
// Poll again and there should be no events
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Read the value
|
||||
assert_eq!("hello", rx.try_recv().unwrap());
|
||||
|
||||
// Poll again, nothing
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Push a value
|
||||
tx.send("goodbye").unwrap();
|
||||
|
||||
// Poll again, nothing
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Reregistering will re-trigger the notification
|
||||
for _ in 0..3 {
|
||||
poll.reregister(
|
||||
&rx,
|
||||
Token(123),
|
||||
Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot(),
|
||||
).unwrap();
|
||||
|
||||
// Have an event
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(1, num);
|
||||
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(event.token(), Token(123));
|
||||
assert_eq!(event.readiness(), Ready::readable());
|
||||
}
|
||||
|
||||
// Get the value
|
||||
assert_eq!("goodbye", rx.try_recv().unwrap());
|
||||
|
||||
poll.reregister(
|
||||
&rx,
|
||||
Token(123),
|
||||
Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot(),
|
||||
).unwrap();
|
||||
|
||||
// Have an event
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
poll.reregister(
|
||||
&rx,
|
||||
Token(123),
|
||||
Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot(),
|
||||
).unwrap();
|
||||
|
||||
// Have an event
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_poll_channel_level() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let (tx, rx) = channel::channel();
|
||||
|
||||
poll.register(&rx, Token(123), Ready::readable(), PollOpt::level())
|
||||
.unwrap();
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Push the value
|
||||
tx.send("hello").unwrap();
|
||||
|
||||
// Polling will contain the event
|
||||
for i in 0..5 {
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert!(1 == num, "actually got {} on iteration {}", num, i);
|
||||
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(event.token(), Token(123));
|
||||
assert_eq!(event.readiness(), Ready::readable());
|
||||
}
|
||||
|
||||
// Read the value
|
||||
assert_eq!("hello", rx.try_recv().unwrap());
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_poll_channel_writable() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let (tx, rx) = channel::channel();
|
||||
|
||||
poll.register(&rx, Token(123), Ready::writable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
|
||||
// Push the value
|
||||
tx.send("hello").unwrap();
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_dropping_receive_before_poll() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let (tx, rx) = channel::channel();
|
||||
|
||||
poll.register(&rx, Token(123), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
// Push the value
|
||||
tx.send("hello").unwrap();
|
||||
|
||||
// Drop the receive end
|
||||
drop(rx);
|
||||
|
||||
// Wait, but nothing should happen
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(0, num);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_mixing_channel_with_socket() {
|
||||
use mio::net::{TcpListener, TcpStream};
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let (tx, rx) = channel::channel();
|
||||
|
||||
// Create the listener
|
||||
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
|
||||
// Register the listener with `Poll`
|
||||
poll.register(&l, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
poll.register(&rx, Token(1), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
// Push a value onto the channel
|
||||
tx.send("hello").unwrap();
|
||||
|
||||
// Connect a TCP socket
|
||||
let s1 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
|
||||
|
||||
// Register the socket
|
||||
poll.register(&s1, Token(2), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
// Sleep a bit to ensure it arrives at dest
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
expect_events(
|
||||
&poll,
|
||||
&mut events,
|
||||
2,
|
||||
vec![
|
||||
Event::new(Ready::empty(), Token(0)),
|
||||
Event::new(Ready::empty(), Token(1)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sending_from_other_thread_while_polling() {
|
||||
const ITERATIONS: usize = 20;
|
||||
const THREADS: usize = 5;
|
||||
|
||||
// Make sure to run multiple times
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
|
||||
for _ in 0..ITERATIONS {
|
||||
let (tx, rx) = channel::channel();
|
||||
poll.register(&rx, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
for _ in 0..THREADS {
|
||||
let tx = tx.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
tx.send("ping").unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
let mut recv = 0;
|
||||
|
||||
while recv < THREADS {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
if num != 0 {
|
||||
assert_eq!(1, num);
|
||||
assert_eq!(events.iter().next().unwrap().token(), Token(0));
|
||||
|
||||
while let Ok(_) = rx.try_recv() {
|
||||
recv += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,304 @@
|
|||
use mio::{Events, Poll, PollOpt, Ready, Token};
|
||||
use mio_extras::timer::{self, Timer};
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_basic_timer_without_poll() {
|
||||
let mut timer = Timer::default();
|
||||
|
||||
// Set the timeout
|
||||
timer.set_timeout(Duration::from_millis(200), "hello");
|
||||
|
||||
// Nothing when polled immediately
|
||||
assert!(timer.poll().is_none());
|
||||
|
||||
// Wait for the timeout
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
assert_eq!(Some("hello"), timer.poll());
|
||||
assert!(timer.poll().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_timer_with_poll_edge_set_timeout_after_register() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
timer.set_timeout(Duration::from_millis(200), "hello");
|
||||
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(200, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("hello", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_timer_with_poll_edge_set_timeout_before_register() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(200), "hello");
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(200, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("hello", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setting_later_timeout_then_earlier_one() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(600), "hello");
|
||||
timer.set_timeout(Duration::from_millis(200), "world");
|
||||
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(200, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("world", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
|
||||
let elapsed = self::elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(400, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("hello", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timer_with_looping_wheel() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = timer::Builder::default().num_slots(2).build();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
const TOKENS: &[&str] = &["hello", "world", "some", "thing"];
|
||||
|
||||
for (i, msg) in TOKENS.iter().enumerate() {
|
||||
timer.set_timeout(Duration::from_millis(500 * (i as u64 + 1)), msg);
|
||||
}
|
||||
|
||||
for msg in TOKENS {
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(
|
||||
is_about(500, elapsed),
|
||||
"actual={:?}; msg={:?}",
|
||||
elapsed,
|
||||
msg
|
||||
);
|
||||
assert_eq!(Some(msg), timer.poll());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_without_polling() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(400), "hello");
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(400, ms), "actual={:?}", ms);
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(num, 0);
|
||||
});
|
||||
|
||||
assert!(is_about(300, ms), "actual={:?}", ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_level_triggered() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::level())
|
||||
.unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(400), "hello");
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(400, ms), "actual={:?}", ms);
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
let event = events.iter().next().unwrap();
|
||||
assert_eq!(Token(0), event.token());
|
||||
assert_eq!(Ready::readable(), event.readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(0, ms), "actual={:?}", ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_oneshot_triggered() {
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(
|
||||
&timer,
|
||||
Token(0),
|
||||
Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot(),
|
||||
).unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(200), "hello");
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
});
|
||||
|
||||
assert!(is_about(200, ms), "actual={:?}", ms);
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300)))
|
||||
.unwrap();
|
||||
assert_eq!(num, 0);
|
||||
});
|
||||
|
||||
assert!(is_about(300, ms), "actual={:?}", ms);
|
||||
|
||||
poll.reregister(
|
||||
&timer,
|
||||
Token(0),
|
||||
Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot(),
|
||||
).unwrap();
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
});
|
||||
|
||||
assert!(is_about(0, ms));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cancel_timeout() {
|
||||
use std::time::Instant;
|
||||
|
||||
let mut timer: Timer<u32> = Default::default();
|
||||
let timeout = timer.set_timeout(Duration::from_millis(200), 1);
|
||||
timer.cancel_timeout(&timeout);
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
let mut events = Events::with_capacity(16);
|
||||
|
||||
let now = Instant::now();
|
||||
let dur = Duration::from_millis(500);
|
||||
let mut i = 0;
|
||||
|
||||
while Instant::now() - now < dur {
|
||||
if i > 10 {
|
||||
panic!("iterated too many times");
|
||||
}
|
||||
|
||||
i += 1;
|
||||
|
||||
let elapsed = Instant::now() - now;
|
||||
|
||||
poll.poll(&mut events, Some(dur - elapsed)).unwrap();
|
||||
|
||||
while let Some(_) = timer.poll() {
|
||||
panic!("did not expect to receive timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn elapsed<F: FnMut()>(mut f: F) -> u64 {
|
||||
use std::time::Instant;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
f();
|
||||
|
||||
let elapsed = now.elapsed();
|
||||
elapsed.as_secs() * 1000 + u64::from(elapsed.subsec_nanos() / 1_000_000)
|
||||
}
|
||||
|
||||
fn is_about(expect: u64, val: u64) -> bool {
|
||||
const WINDOW: i64 = 200;
|
||||
|
||||
((expect as i64) - (val as i64)).abs() <= WINDOW
|
||||
}
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,3 +1,8 @@
|
|||
# 0.6.16 (September 5, 2018)
|
||||
|
||||
* Add EPOLLPRI readiness to UnixReady on supported platforms (#867)
|
||||
* Reduce spurious awaken calls (#875)
|
||||
|
||||
# 0.6.15 (July 3, 2018)
|
||||
|
||||
* Implement `Evented` for containers (#840).
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
|
||||
[package]
|
||||
name = "mio"
|
||||
version = "0.6.15"
|
||||
version = "0.6.16"
|
||||
authors = ["Carl Lerche <me@carllerche.com>"]
|
||||
exclude = [".gitignore", ".travis.yml", "deploy.sh"]
|
||||
description = "Lightweight non-blocking IO"
|
||||
homepage = "https://github.com/carllerche/mio"
|
||||
documentation = "https://docs.rs/mio"
|
||||
documentation = "https://docs.rs/mio/0.6.16/mio/"
|
||||
readme = "README.md"
|
||||
keywords = ["io", "async", "non-blocking"]
|
||||
categories = ["asynchronous"]
|
||||
|
@ -31,7 +31,7 @@ path = "test/mod.rs"
|
|||
version = "0.1.1"
|
||||
|
||||
[dependencies.lazycell]
|
||||
version = "0.6.0"
|
||||
version = "1"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
|
|
@ -1,202 +0,0 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// This is a script to deploy and execute a binary on an iOS simulator.
|
||||
// The primary use of this is to be able to run unit tests on the simulator and
|
||||
// retrieve the results.
|
||||
//
|
||||
// To do this through Cargo instead, use Dinghy
|
||||
// (https://github.com/snipsco/dinghy): cargo dinghy install, then cargo dinghy
|
||||
// test.
|
||||
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::thread;
|
||||
use std::process::{self, Command, Stdio};
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with: {}", stringify!($e), e),
|
||||
})
|
||||
}
|
||||
|
||||
// Step one: Wrap as an app
|
||||
fn package_as_simulator_app(crate_name: &str, test_binary_path: &Path) {
|
||||
println!("Packaging simulator app");
|
||||
drop(fs::remove_dir_all("ios_simulator_app"));
|
||||
t!(fs::create_dir("ios_simulator_app"));
|
||||
t!(fs::copy(test_binary_path,
|
||||
Path::new("ios_simulator_app").join(crate_name)));
|
||||
|
||||
let mut f = t!(File::create("ios_simulator_app/Info.plist"));
|
||||
t!(f.write_all(format!(r#"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC
|
||||
"-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.rust.unittests</string>
|
||||
</dict>
|
||||
</plist>
|
||||
"#, crate_name).as_bytes()));
|
||||
}
|
||||
|
||||
// Step two: Start the iOS simulator
|
||||
fn start_simulator() {
|
||||
println!("Looking for iOS simulator");
|
||||
let output = t!(Command::new("xcrun").arg("simctl").arg("list").output());
|
||||
assert!(output.status.success());
|
||||
let mut simulator_exists = false;
|
||||
let mut simulator_booted = false;
|
||||
let mut found_rust_sim = false;
|
||||
let stdout = t!(String::from_utf8(output.stdout));
|
||||
for line in stdout.lines() {
|
||||
if line.contains("rust_ios") {
|
||||
if found_rust_sim {
|
||||
panic!("Duplicate rust_ios simulators found. Please \
|
||||
double-check xcrun simctl list.");
|
||||
}
|
||||
simulator_exists = true;
|
||||
simulator_booted = line.contains("(Booted)");
|
||||
found_rust_sim = true;
|
||||
}
|
||||
}
|
||||
|
||||
if simulator_exists == false {
|
||||
println!("Creating iOS simulator");
|
||||
Command::new("xcrun")
|
||||
.arg("simctl")
|
||||
.arg("create")
|
||||
.arg("rust_ios")
|
||||
.arg("com.apple.CoreSimulator.SimDeviceType.iPhone-SE")
|
||||
.arg("com.apple.CoreSimulator.SimRuntime.iOS-10-2")
|
||||
.check_status();
|
||||
} else if simulator_booted == true {
|
||||
println!("Shutting down already-booted simulator");
|
||||
Command::new("xcrun")
|
||||
.arg("simctl")
|
||||
.arg("shutdown")
|
||||
.arg("rust_ios")
|
||||
.check_status();
|
||||
}
|
||||
|
||||
println!("Starting iOS simulator");
|
||||
// We can't uninstall the app (if present) as that will hang if the
|
||||
// simulator isn't completely booted; just erase the simulator instead.
|
||||
Command::new("xcrun").arg("simctl").arg("erase").arg("rust_ios").check_status();
|
||||
Command::new("xcrun").arg("simctl").arg("boot").arg("rust_ios").check_status();
|
||||
}
|
||||
|
||||
// Step three: Install the app
|
||||
fn install_app_to_simulator() {
|
||||
println!("Installing app to simulator");
|
||||
Command::new("xcrun")
|
||||
.arg("simctl")
|
||||
.arg("install")
|
||||
.arg("booted")
|
||||
.arg("ios_simulator_app/")
|
||||
.check_status();
|
||||
}
|
||||
|
||||
// Step four: Run the app
|
||||
fn run_app_on_simulator() {
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
println!("Running app");
|
||||
let mut child = t!(Command::new("xcrun")
|
||||
.arg("simctl")
|
||||
.arg("launch")
|
||||
.arg("--console")
|
||||
.arg("booted")
|
||||
.arg("com.rust.unittests")
|
||||
.arg("--color")
|
||||
.arg("never")
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn());
|
||||
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let stderr = child.stderr.take().unwrap();
|
||||
|
||||
let th = thread::spawn(move || {
|
||||
let mut out = vec![];
|
||||
|
||||
for b in stdout.bytes() {
|
||||
let b = b.unwrap();
|
||||
out.push(b);
|
||||
let out = [b];
|
||||
io::stdout().write(&out[..]).unwrap();
|
||||
}
|
||||
|
||||
out
|
||||
});
|
||||
|
||||
thread::spawn(move || {
|
||||
for b in stderr.bytes() {
|
||||
let out = [b.unwrap()];
|
||||
io::stderr().write(&out[..]).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
println!("Waiting for cmd to finish");
|
||||
child.wait().unwrap();
|
||||
|
||||
println!("Waiting for stdout");
|
||||
let stdout = th.join().unwrap();
|
||||
let stdout = String::from_utf8_lossy(&stdout);
|
||||
let passed = stdout.lines()
|
||||
.find(|l| l.contains("test result"))
|
||||
.map(|l| l.contains(" 0 failed"))
|
||||
.unwrap_or(false);
|
||||
|
||||
println!("Shutting down simulator");
|
||||
Command::new("xcrun")
|
||||
.arg("simctl")
|
||||
.arg("shutdown")
|
||||
.arg("rust_ios")
|
||||
.check_status();
|
||||
if !passed {
|
||||
panic!("tests didn't pass");
|
||||
}
|
||||
}
|
||||
|
||||
trait CheckStatus {
|
||||
fn check_status(&mut self);
|
||||
}
|
||||
|
||||
impl CheckStatus for Command {
|
||||
fn check_status(&mut self) {
|
||||
println!("\trunning: {:?}", self);
|
||||
assert!(t!(self.status()).success());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("Usage: {} <executable>", args[0]);
|
||||
process::exit(-1);
|
||||
}
|
||||
|
||||
let test_binary_path = Path::new(&args[1]);
|
||||
let crate_name = test_binary_path.file_name().unwrap();
|
||||
|
||||
package_as_simulator_app(crate_name.to_str().unwrap(), test_binary_path);
|
||||
start_simulator();
|
||||
install_app_to_simulator();
|
||||
run_app_on_simulator();
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Builds and runs tests for a particular target passed as an argument to this
|
||||
# script.
|
||||
|
||||
set -ex
|
||||
|
||||
TARGET=$1
|
||||
|
||||
|
||||
case "$TARGET" in
|
||||
*-apple-ios)
|
||||
# Download the iOS test harness
|
||||
curl -vv -L https://github.com/carllerche/ios-test-harness/releases/download/v0.1.0/libiosharness-$TARGET.a > libiosharness.a;
|
||||
|
||||
# Build the test
|
||||
cargo rustc --test test --target $TARGET -- \
|
||||
-L . \
|
||||
-C link-args="-mios-simulator-version-min=7.0 -e _ios_main -liosharness";
|
||||
|
||||
|
||||
# Find the file to run
|
||||
TEST_FILE="$(find $TARGET/debug -maxdepth 1 -type f -name test-* | head -1)";
|
||||
|
||||
rustc -O ./ci/ios/deploy_and_run_on_ios_simulator.rs;
|
||||
./deploy_and_run_on_ios_simulator $TEST_FILE;
|
||||
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "unsupported target $TARGET";
|
||||
exit 1;
|
||||
;;
|
||||
esac
|
|
@ -718,6 +718,8 @@ impl Ready {
|
|||
/// let ready = Ready::empty();
|
||||
/// assert!(ready.is_empty());
|
||||
/// ```
|
||||
///
|
||||
/// [`Poll`]: struct.Poll.html
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
*self == Ready::empty()
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#![doc(html_root_url = "https://docs.rs/mio/0.6.16")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! A fast, low-level IO library for Rust focusing on non-blocking APIs, event
|
||||
//! notification, and other useful utilities for building high performance IO
|
||||
//! apps.
|
||||
|
@ -90,11 +94,6 @@
|
|||
//!
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/mio/0.6.15")]
|
||||
#![crate_name = "mio"]
|
||||
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
|
||||
extern crate lazycell;
|
||||
extern crate net2;
|
||||
extern crate iovec;
|
||||
|
|
|
@ -695,8 +695,8 @@ impl Poll {
|
|||
/// readiness. `Poll` will only return readiness events for operations
|
||||
/// specified by this argument.
|
||||
///
|
||||
/// If a socket is registered with [`readable`] interest and the socket
|
||||
/// becomes writable, no event will be returned from [`poll`].
|
||||
/// If a socket is registered with readable interest and the socket becomes
|
||||
/// writable, no event will be returned from [`poll`].
|
||||
///
|
||||
/// The readiness interest for an `Evented` handle can be changed at any
|
||||
/// time by calling [`reregister`].
|
||||
|
@ -860,7 +860,7 @@ impl Poll {
|
|||
///
|
||||
/// When an `Evented` handle is deregistered, the `Poll` instance will
|
||||
/// no longer monitor it for readiness state changes. Unlike disabling
|
||||
/// handles with [`oneshot`], deregistering clears up any internal resources
|
||||
/// handles with oneshot, deregistering clears up any internal resources
|
||||
/// needed to track the handle.
|
||||
///
|
||||
/// A handle can be passed back to `register` after it has been
|
||||
|
@ -1157,6 +1157,8 @@ impl Poll {
|
|||
if timeout == Some(Duration::from_millis(0)) {
|
||||
// If blocking is not requested, then there is no need to prepare
|
||||
// the queue for sleep
|
||||
//
|
||||
// The sleep_marker should be removed by readiness_queue.poll().
|
||||
} else if self.readiness_queue.prepare_for_sleep() {
|
||||
// The readiness queue is empty. The call to `prepare_for_sleep`
|
||||
// inserts `sleep_marker` into the queue. This signals to any
|
||||
|
@ -2116,6 +2118,13 @@ impl ReadinessQueue {
|
|||
// loop where `Poll::poll` will keep dequeuing nodes it enqueues.
|
||||
let mut until = ptr::null_mut();
|
||||
|
||||
if dst.len() == dst.capacity() {
|
||||
// If `dst` is already full, the readiness queue won't be drained.
|
||||
// This might result in `sleep_marker` staying in the queue and
|
||||
// unecessary pipe writes occuring.
|
||||
self.inner.clear_sleep_marker();
|
||||
}
|
||||
|
||||
'outer:
|
||||
while dst.len() < dst.capacity() {
|
||||
// Dequeue a node. If the queue is in an inconsistent state, then
|
||||
|
@ -2353,6 +2362,37 @@ impl ReadinessQueueInner {
|
|||
}
|
||||
}
|
||||
|
||||
fn clear_sleep_marker(&self) {
|
||||
let end_marker = self.end_marker();
|
||||
let sleep_marker = self.sleep_marker();
|
||||
|
||||
unsafe {
|
||||
let tail = *self.tail_readiness.get();
|
||||
|
||||
if tail != self.sleep_marker() {
|
||||
return;
|
||||
}
|
||||
|
||||
// The empty markeer is *not* currently in the readiness queue
|
||||
// (since the sleep markeris).
|
||||
self.end_marker.next_readiness.store(ptr::null_mut(), Relaxed);
|
||||
|
||||
let actual = self.head_readiness.compare_and_swap(
|
||||
sleep_marker, end_marker, AcqRel);
|
||||
|
||||
debug_assert!(actual != end_marker);
|
||||
|
||||
if actual != sleep_marker {
|
||||
// The readiness queue is not empty, we cannot remove the sleep
|
||||
// markeer
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the tail pointer.
|
||||
*self.tail_readiness.get() = end_marker;
|
||||
}
|
||||
}
|
||||
|
||||
/// Must only be called in `poll` or `drop`
|
||||
unsafe fn dequeue_node(&self, until: *mut ReadinessNode) -> Dequeue {
|
||||
// This is the 1024cores.net intrusive MPSC queue [1] "pop" function
|
||||
|
@ -2362,6 +2402,10 @@ impl ReadinessQueueInner {
|
|||
|
||||
if tail == self.end_marker() || tail == self.sleep_marker() || tail == self.closed_marker() {
|
||||
if next.is_null() {
|
||||
// Make sure the sleep marker is removed (as we are no longer
|
||||
// sleeping
|
||||
self.clear_sleep_marker();
|
||||
|
||||
return Dequeue::Empty;
|
||||
}
|
||||
|
||||
|
|
|
@ -145,6 +145,11 @@ fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> u32 {
|
|||
kind |= EPOLLRDHUP;
|
||||
}
|
||||
|
||||
|
||||
if UnixReady::from(interest).is_priority() {
|
||||
kind |= EPOLLPRI;
|
||||
}
|
||||
|
||||
if opts.is_edge() {
|
||||
kind |= EPOLLET;
|
||||
}
|
||||
|
@ -206,10 +211,14 @@ impl Events {
|
|||
let epoll = event.events as c_int;
|
||||
let mut kind = Ready::empty();
|
||||
|
||||
if (epoll & EPOLLIN) != 0 || (epoll & EPOLLPRI) != 0 {
|
||||
if (epoll & EPOLLIN) != 0 {
|
||||
kind = kind | Ready::readable();
|
||||
}
|
||||
|
||||
if (epoll & EPOLLPRI) != 0 {
|
||||
kind = kind | Ready::readable() | UnixReady::priority();
|
||||
}
|
||||
|
||||
if (epoll & EPOLLOUT) != 0 {
|
||||
kind = kind | Ready::writable();
|
||||
}
|
||||
|
|
|
@ -109,6 +109,11 @@ const LIO: usize = 0b100000;
|
|||
#[cfg(not(any(target_os = "freebsd")))]
|
||||
const LIO: usize = 0b000000;
|
||||
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
|
||||
const PRI: usize = ::libc::EPOLLPRI as usize;
|
||||
|
||||
|
||||
// Export to support `Ready::all`
|
||||
pub const READY_ALL: usize = ERROR | HUP | AIO | LIO;
|
||||
|
||||
|
@ -219,6 +224,28 @@ impl UnixReady {
|
|||
UnixReady(ready_from_usize(LIO))
|
||||
}
|
||||
|
||||
/// Returns a `Ready` representing priority (`EPOLLPRI`) readiness
|
||||
///
|
||||
/// See [`Poll`] for more documentation on polling.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use mio::unix::UnixReady;
|
||||
///
|
||||
/// let ready = UnixReady::priority();
|
||||
///
|
||||
/// assert!(ready.is_priority());
|
||||
/// ```
|
||||
///
|
||||
/// [`Poll`]: struct.Poll.html
|
||||
#[inline]
|
||||
#[cfg(any(target_os = "linux",
|
||||
target_os = "android", target_os = "solaris"))]
|
||||
pub fn priority() -> UnixReady {
|
||||
UnixReady(ready_from_usize(PRI))
|
||||
}
|
||||
|
||||
/// Returns true if `Ready` contains AIO readiness
|
||||
///
|
||||
/// See [`Poll`] for more documentation on polling.
|
||||
|
@ -323,6 +350,28 @@ impl UnixReady {
|
|||
pub fn is_lio(&self) -> bool {
|
||||
self.contains(ready_from_usize(LIO))
|
||||
}
|
||||
|
||||
/// Returns true if `Ready` contains priority (`EPOLLPRI`) readiness
|
||||
///
|
||||
/// See [`Poll`] for more documentation on polling.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use mio::unix::UnixReady;
|
||||
///
|
||||
/// let ready = UnixReady::priority();
|
||||
///
|
||||
/// assert!(ready.is_priority());
|
||||
/// ```
|
||||
///
|
||||
/// [`Poll`]: struct.Poll.html
|
||||
#[inline]
|
||||
#[cfg(any(target_os = "linux",
|
||||
target_os = "android", target_os = "solaris"))]
|
||||
pub fn is_priority(&self) -> bool {
|
||||
self.contains(ready_from_usize(PRI))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ready> for UnixReady {
|
||||
|
@ -408,7 +457,11 @@ impl fmt::Debug for UnixReady {
|
|||
(UnixReady::error(), "Error"),
|
||||
(UnixReady::hup(), "Hup"),
|
||||
#[allow(deprecated)]
|
||||
(UnixReady::aio(), "Aio")];
|
||||
(UnixReady::aio(), "Aio"),
|
||||
#[cfg(any(target_os = "linux",
|
||||
target_os = "android", target_os = "solaris"))]
|
||||
(UnixReady::priority(), "Priority"),
|
||||
];
|
||||
|
||||
for &(flag, msg) in &flags {
|
||||
if self.contains(flag) {
|
||||
|
|
|
@ -45,9 +45,6 @@ mod test_tick;
|
|||
// platforms that were supported from before the features were deprecated
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
#[cfg(feature = "with-deprecated")]
|
||||
mod test_timer;
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
#[cfg(feature = "with-deprecated")]
|
||||
mod test_battery;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
|
|
|
@ -1,433 +0,0 @@
|
|||
use {sleep_ms, TryRead, TryWrite};
|
||||
use mio::*;
|
||||
use mio::deprecated::{EventLoop, Handler};
|
||||
use mio::timer::{Timer};
|
||||
|
||||
use mio::net::{TcpListener, TcpStream};
|
||||
use bytes::{Buf, ByteBuf, SliceBuf};
|
||||
use localhost;
|
||||
use std::time::Duration;
|
||||
|
||||
use self::TestState::{Initial, AfterRead};
|
||||
|
||||
#[test]
|
||||
fn test_basic_timer_without_poll() {
|
||||
let mut timer = Timer::default();
|
||||
|
||||
// Set the timeout
|
||||
timer.set_timeout(Duration::from_millis(200), "hello").unwrap();
|
||||
|
||||
// Nothing when polled immediately
|
||||
assert!(timer.poll().is_none());
|
||||
|
||||
// Wait for the timeout
|
||||
sleep_ms(200);
|
||||
|
||||
assert_eq!(Some("hello"), timer.poll());
|
||||
assert!(timer.poll().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_timer_with_poll_edge_set_timeout_after_register() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
timer.set_timeout(Duration::from_millis(200), "hello").unwrap();
|
||||
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(200, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("hello", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_timer_with_poll_edge_set_timeout_before_register() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(200), "hello").unwrap();
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(200, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("hello", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setting_later_timeout_then_earlier_one() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(600), "hello").unwrap();
|
||||
timer.set_timeout(Duration::from_millis(200), "world").unwrap();
|
||||
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(200, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("world", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
|
||||
let elapsed = self::elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(400, elapsed), "actual={:?}", elapsed);
|
||||
assert_eq!("hello", timer.poll().unwrap());
|
||||
assert_eq!(None, timer.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timer_with_looping_wheel() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = timer::Builder::default()
|
||||
.num_slots(2)
|
||||
.build();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
|
||||
const TOKENS: &'static [ &'static str ] = &[ "hello", "world", "some", "thing" ];
|
||||
|
||||
for (i, msg) in TOKENS.iter().enumerate() {
|
||||
timer.set_timeout(Duration::from_millis(500 * (i as u64 + 1)), msg).unwrap();
|
||||
}
|
||||
|
||||
for msg in TOKENS {
|
||||
let elapsed = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(500, elapsed), "actual={:?}; msg={:?}", elapsed, msg);
|
||||
assert_eq!(Some(msg), timer.poll());
|
||||
assert_eq!(None, timer.poll());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_without_polling() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(400), "hello").unwrap();
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(400, ms), "actual={:?}", ms);
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
|
||||
assert_eq!(num, 0);
|
||||
});
|
||||
|
||||
assert!(is_about(300, ms), "actual={:?}", ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_level_triggered() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::level()).unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(400), "hello").unwrap();
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(400, ms), "actual={:?}", ms);
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
assert_eq!(Token(0), events.get(0).unwrap().token());
|
||||
assert_eq!(Ready::readable(), events.get(0).unwrap().readiness());
|
||||
});
|
||||
|
||||
assert!(is_about(0, ms), "actual={:?}", ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_oneshot_triggered() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut timer = Timer::default();
|
||||
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
|
||||
|
||||
timer.set_timeout(Duration::from_millis(200), "hello").unwrap();
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
});
|
||||
|
||||
assert!(is_about(200, ms), "actual={:?}", ms);
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
|
||||
assert_eq!(num, 0);
|
||||
});
|
||||
|
||||
assert!(is_about(300, ms), "actual={:?}", ms);
|
||||
|
||||
poll.reregister(&timer, Token(0), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
|
||||
|
||||
let ms = elapsed(|| {
|
||||
let num = poll.poll(&mut events, None).unwrap();
|
||||
assert_eq!(num, 1);
|
||||
});
|
||||
|
||||
assert!(is_about(0, ms));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cancel_timeout() {
|
||||
use std::time::Instant;
|
||||
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let mut timer: Timer<u32> = Default::default();
|
||||
let timeout = timer.set_timeout(Duration::from_millis(200), 1).unwrap();
|
||||
timer.cancel_timeout(&timeout);
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
poll.register(&timer, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
|
||||
let mut events = Events::with_capacity(16);
|
||||
|
||||
let now = Instant::now();
|
||||
let dur = Duration::from_millis(500);
|
||||
let mut i = 0;
|
||||
|
||||
while Instant::now() - now < dur {
|
||||
if i > 10 {
|
||||
panic!("iterated too many times");
|
||||
}
|
||||
|
||||
i += 1;
|
||||
|
||||
let elapsed = Instant::now() - now;
|
||||
|
||||
poll.poll(&mut events, Some(dur - elapsed)).unwrap();
|
||||
|
||||
while let Some(_) = timer.poll() {
|
||||
panic!("did not expect to receive timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn elapsed<F: FnMut()>(mut f: F) -> u64 {
|
||||
use std::time::Instant;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
f();
|
||||
|
||||
let elapsed = now.elapsed();
|
||||
elapsed.as_secs() * 1000 + (elapsed.subsec_nanos() / 1_000_000) as u64
|
||||
}
|
||||
|
||||
fn is_about(expect: u64, val: u64) -> bool {
|
||||
const WINDOW: i64 = 200;
|
||||
|
||||
((expect as i64) - (val as i64)).abs() <= WINDOW
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ===== OLD TIMER =====
|
||||
*
|
||||
*/
|
||||
|
||||
const SERVER: Token = Token(0);
|
||||
const CLIENT: Token = Token(1);
|
||||
const CONN: Token = Token(2);
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum TestState {
|
||||
Initial,
|
||||
AfterRead,
|
||||
}
|
||||
|
||||
struct TestHandler {
|
||||
srv: TcpListener,
|
||||
cli: TcpStream,
|
||||
state: TestState
|
||||
}
|
||||
|
||||
impl TestHandler {
|
||||
fn new(srv: TcpListener, cli: TcpStream) -> TestHandler {
|
||||
TestHandler {
|
||||
srv: srv,
|
||||
cli: cli,
|
||||
state: Initial
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_read(&mut self, event_loop: &mut EventLoop<TestHandler>,
|
||||
tok: Token, _events: Ready) {
|
||||
match tok {
|
||||
SERVER => {
|
||||
debug!("server connection ready for accept");
|
||||
let conn = self.srv.accept().unwrap().0;
|
||||
event_loop.register(&conn, CONN, Ready::readable() | Ready::writable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
event_loop.timeout(conn, Duration::from_millis(200)).unwrap();
|
||||
|
||||
event_loop.reregister(&self.srv, SERVER, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
}
|
||||
CLIENT => {
|
||||
debug!("client readable");
|
||||
|
||||
match self.state {
|
||||
Initial => self.state = AfterRead,
|
||||
AfterRead => {}
|
||||
}
|
||||
|
||||
let mut buf = ByteBuf::mut_with_capacity(2048);
|
||||
|
||||
match self.cli.try_read_buf(&mut buf) {
|
||||
Ok(Some(0)) => return event_loop.shutdown(),
|
||||
Ok(n) => {
|
||||
debug!("read {:?} bytes", n);
|
||||
assert!(b"zomg" == buf.flip().bytes());
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("client sock failed to read; err={:?}", e.kind());
|
||||
}
|
||||
}
|
||||
|
||||
event_loop.reregister(&self.cli, CLIENT,
|
||||
Ready::readable() | Ready::hup(),
|
||||
PollOpt::edge()).unwrap();
|
||||
}
|
||||
CONN => {}
|
||||
_ => panic!("received unknown token {:?}", tok),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_write(&mut self, event_loop: &mut EventLoop<TestHandler>,
|
||||
tok: Token, _: Ready) {
|
||||
match tok {
|
||||
SERVER => panic!("received writable for token 0"),
|
||||
CLIENT => debug!("client connected"),
|
||||
CONN => {}
|
||||
_ => panic!("received unknown token {:?}", tok),
|
||||
}
|
||||
|
||||
event_loop.reregister(&self.cli, CLIENT, Ready::readable(),
|
||||
PollOpt::edge()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for TestHandler {
|
||||
type Timeout = TcpStream;
|
||||
type Message = ();
|
||||
|
||||
fn ready(&mut self, event_loop: &mut EventLoop<TestHandler>, tok: Token, events: Ready) {
|
||||
if events.is_readable() {
|
||||
self.handle_read(event_loop, tok, events);
|
||||
}
|
||||
|
||||
if events.is_writable() {
|
||||
self.handle_write(event_loop, tok, events);
|
||||
}
|
||||
}
|
||||
|
||||
fn timeout(&mut self, _event_loop: &mut EventLoop<TestHandler>, mut sock: TcpStream) {
|
||||
debug!("timeout handler : writing to socket");
|
||||
sock.try_write_buf(&mut SliceBuf::wrap(b"zomg")).unwrap().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_old_timer() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
debug!("Starting TEST_TIMER");
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let addr = localhost();
|
||||
|
||||
let srv = TcpListener::bind(&addr).unwrap();
|
||||
|
||||
info!("listening for connections");
|
||||
|
||||
event_loop.register(&srv, SERVER, Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
|
||||
|
||||
let sock = TcpStream::connect(&addr).unwrap();
|
||||
|
||||
// Connect to the server
|
||||
event_loop.register(&sock, CLIENT, Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
|
||||
|
||||
// Init the handler
|
||||
let mut handler = TestHandler::new(srv, sock);
|
||||
// Start the event loop
|
||||
event_loop.run(&mut handler).unwrap();
|
||||
|
||||
assert!(handler.state == AfterRead, "actual={:?}", handler.state);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"460c7dbfdf227b4c725f7da8131fd51a54d0355b3c5804c0d388c70d3c1c49ec","Cargo.toml":"7d29da51fe4bf73964b3b3dea0af88040514569e2d184c9e8eb2f1746d540fb7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"fb8071c3bc1013107b16ebcb303f31ef614e81440f2d58a46bfb9ff1e311b792","appveyor.yml":"8796156caf7041ef2a43f7a313df21ea639de3f2563b6181bba1096b1c489f1b","benches/bench.rs":"35c4ab609f2a5f5aab6c52c257415258dc0780621b492b5a82bb12d048cab6db","benches/distributions/exponential.rs":"99cb59c013a0b6bb390d34c5649b341fc3b88ea7df0caf2470bdda8798f9fe3d","benches/distributions/gamma.rs":"3533f311e4b55d743c5b01a7eb6529c94fd97726ef6702a6372f914f5f33666b","benches/distributions/mod.rs":"0028f1cb96f61152ed5b49a4fe91227d809ef6d19035592c36032a538af7f95e","benches/distributions/normal.rs":"4e10c18cb583ccb96301ea953c8e0aa9ee3b6662060271d1b8d19ca23364dc6b","benches/generators.rs":"aaa2f1dbfb399df8323d8a5796b92add6210cd5f0f1d916895ffdd81d60f812b","benches/misc.rs":"bd2f7c5a16f0fcb59022d5aeef66ed3c94e89ebf6c06667851dd23d0b1595504","src/distributions/exponential.rs":"103c8412c8a581b71835f1c00e40f6370e7702adf9d499243933a793d132d4e7","src/distributions/gamma.rs":"7a3f85c8daad4e56e334586ddb9fc9d83df3b0699738ed681a6c41e4ed455be9","src/distributions/mod.rs":"7943c4f83721bac816f831cca3b1574b6136932f7b4927aa6101130080ba62c5","src/distributions/normal.rs":"1562b43f80e4d5f83a8deb5af18de5a18dfeeeeda11fefc577da26672b14c949","src/distributions/range.rs":"a72a538d3ec4ed23f8d632aa55fd4793c464f24a5872d04ce8095ddd5db92115","src/distributions/ziggurat_tables.rs":"4eacf94fc352c91c455a6623de6a721e53842e1690f13a5662b6a79c7fbb73de","src/jitter.rs":"befd4b84bf753c107370b5b9498ad49611c220bdae2e4be9ee4398e9fa497042","src/lib.rs":"fbdc5f56ce1a52b15c85b0aa70a555c64be8f65d9f6f90fa0a3555d7862666b4","src/os.rs":"4860f165f68b7c978b0488c75d264cd9aaf54e7e4484036736ee5c4f5b6bd78d","src/prng/chacha.rs":"558007276f9c22933d39e5b8e853f4dd9533e823ed66df8dc1f23ad6925b1d51","src/prng/isaac.rs":"a8a2ee8b38d312663308e3bdf03376e342fd91330655f39144e5bba7392b2a8e","src/prng/isaac64.rs":"f28f7596ccab910db265b42671116abb9d2039fa8a421cbc75312bd0e7715d3a","src/prng/mod.rs":"c1a73450f49e819a20942a5b591f84a08ebb5ac33aa0f65b18ac1dc9a19a3084","src/prng/xorshift.rs":"606c308747293652c868b46dc3cad847d0c3717629c04ba75681c887c7634114","src/rand_impls.rs":"e1f27077fc13d5855bb66235f8ccfb216e116337eb38424d9c30c090e112215c","src/read.rs":"bd0eb508a6b659dc578d546fc2f231484aed80c73cfe8c475e0d65c8d699a769","src/reseeding.rs":"a97b86387b87ea1adc5262ddea480fe735c9c2a86762abaace29119022ac9f6e","src/seq.rs":"76dd58af0f580aed2721c393a5c036322186dc7cb3b4abed33436620c7c49288","utils/ziggurat_tables.py":"a9fc0a2fdae9b5c798c238788f94b720c156e13fd96f2356c409aa533191eb94"},"package":"8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"}
|
|
@ -0,0 +1,269 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.4.3] - 2018-08-16
|
||||
### Fixed
|
||||
- Use correct syscall number for PowerPC (#589)
|
||||
|
||||
## [0.4.2] - 2018-01-05
|
||||
### Changed
|
||||
- Use winapi on Windows
|
||||
- Update for Fuchsia OS
|
||||
- Remove dev-dependency on `log`
|
||||
|
||||
## [0.4.1] - 2017-12-17
|
||||
### Added
|
||||
- `no_std` support
|
||||
|
||||
## [0.4.0-pre.0] - 2017-12-11
|
||||
### Added
|
||||
- `JitterRng` added as a high-quality alternative entropy source using the
|
||||
system timer
|
||||
- new `seq` module with `sample_iter`, `sample_slice`, etc.
|
||||
- WASM support via dummy implementations (fail at run-time)
|
||||
- Additional benchmarks, covering generators and new seq code
|
||||
|
||||
### Changed
|
||||
- `thread_rng` uses `JitterRng` if seeding from system time fails
|
||||
(slower but more secure than previous method)
|
||||
|
||||
### Deprecated
|
||||
- `sample` function deprecated (replaced by `sample_iter`)
|
||||
|
||||
## [0.3.18] - 2017-11-06
|
||||
### Changed
|
||||
- `thread_rng` is seeded from the system time if `OsRng` fails
|
||||
- `weak_rng` now uses `thread_rng` internally
|
||||
|
||||
|
||||
## [0.3.17] - 2017-10-07
|
||||
### Changed
|
||||
- Fuchsia: Magenta was renamed Zircon
|
||||
|
||||
## [0.3.16] - 2017-07-27
|
||||
### Added
|
||||
- Implement Debug for mote non-public types
|
||||
- implement `Rand` for (i|u)i128
|
||||
- Support for Fuchsia
|
||||
|
||||
### Changed
|
||||
- Add inline attribute to SampleRange::construct_range.
|
||||
This improves the benchmark for sample in 11% and for shuffle in 16%.
|
||||
- Use `RtlGenRandom` instead of `CryptGenRandom`
|
||||
|
||||
|
||||
## [0.3.15] - 2016-11-26
|
||||
### Added
|
||||
- Add `Rng` trait method `choose_mut`
|
||||
- Redox support
|
||||
|
||||
### Changed
|
||||
- Use `arc4rand` for `OsRng` on FreeBSD.
|
||||
- Use `arc4random(3)` for `OsRng` on OpenBSD.
|
||||
|
||||
### Fixed
|
||||
- Fix filling buffers 4 GiB or larger with `OsRng::fill_bytes` on Windows
|
||||
|
||||
|
||||
## [0.3.14] - 2016-02-13
|
||||
### Fixed
|
||||
- Inline definitions from winapi/advapi32, wich decreases build times
|
||||
|
||||
|
||||
## [0.3.13] - 2016-01-09
|
||||
### Fixed
|
||||
- Compatible with Rust 1.7.0-nightly (needed some extra type annotations)
|
||||
|
||||
|
||||
## [0.3.12] - 2015-11-09
|
||||
### Changed
|
||||
- Replaced the methods in `next_f32` and `next_f64` with the technique described
|
||||
Saito & Matsumoto at MCQMC'08. The new method should exhibit a slightly more
|
||||
uniform distribution.
|
||||
- Depend on libc 0.2
|
||||
|
||||
### Fixed
|
||||
- Fix iterator protocol issue in `rand::sample`
|
||||
|
||||
|
||||
## [0.3.11] - 2015-08-31
|
||||
### Added
|
||||
- Implement `Rand` for arrays with n <= 32
|
||||
|
||||
|
||||
## [0.3.10] - 2015-08-17
|
||||
### Added
|
||||
- Support for NaCl platforms
|
||||
|
||||
### Changed
|
||||
- Allow `Rng` to be `?Sized`, impl for `&mut R` and `Box<R>` where `R: ?Sized + Rng`
|
||||
|
||||
|
||||
## [0.3.9] - 2015-06-18
|
||||
### Changed
|
||||
- Use `winapi` for Windows API things
|
||||
|
||||
### Fixed
|
||||
- Fixed test on stable/nightly
|
||||
- Fix `getrandom` syscall number for aarch64-unknown-linux-gnu
|
||||
|
||||
|
||||
## [0.3.8] - 2015-04-23
|
||||
### Changed
|
||||
- `log` is a dev dependency
|
||||
|
||||
### Fixed
|
||||
- Fix race condition of atomics in `is_getrandom_available`
|
||||
|
||||
|
||||
## [0.3.7] - 2015-04-03
|
||||
### Fixed
|
||||
- Derive Copy/Clone changes
|
||||
|
||||
|
||||
## [0.3.6] - 2015-04-02
|
||||
### Changed
|
||||
- Move to stable Rust!
|
||||
|
||||
|
||||
## [0.3.5] - 2015-04-01
|
||||
### Fixed
|
||||
- Compatible with Rust master
|
||||
|
||||
|
||||
## [0.3.4] - 2015-03-31
|
||||
### Added
|
||||
- Implement Clone for `Weighted`
|
||||
|
||||
### Fixed
|
||||
- Compatible with Rust master
|
||||
|
||||
|
||||
## [0.3.3] - 2015-03-26
|
||||
### Fixed
|
||||
- Fix compile on Windows
|
||||
|
||||
|
||||
## [0.3.2] - 2015-03-26
|
||||
|
||||
|
||||
## [0.3.1] - 2015-03-26
|
||||
### Fixed
|
||||
- Fix compile on Windows
|
||||
|
||||
|
||||
## [0.3.0] - 2015-03-25
|
||||
### Changed
|
||||
- Update to use log version 0.3.x
|
||||
|
||||
|
||||
## [0.2.1] - 2015-03-22
|
||||
### Fixed
|
||||
- Compatible with Rust master
|
||||
- Fixed iOS compilation
|
||||
|
||||
|
||||
## [0.2.0] - 2015-03-06
|
||||
### Fixed
|
||||
- Compatible with Rust master (move from `old_io` to `std::io`)
|
||||
|
||||
|
||||
## [0.1.4] - 2015-03-04
|
||||
### Fixed
|
||||
- Compatible with Rust master (use wrapping ops)
|
||||
|
||||
|
||||
## [0.1.3] - 2015-02-20
|
||||
### Fixed
|
||||
- Compatible with Rust master
|
||||
|
||||
### Removed
|
||||
- Removed Copy inplementaions from RNGs
|
||||
|
||||
|
||||
## [0.1.2] - 2015-02-03
|
||||
### Added
|
||||
- Imported functionality from `std::rand`, including:
|
||||
- `StdRng`, `SeedableRng`, `TreadRng`, `weak_rng()`
|
||||
- `ReaderRng`: A wrapper around any Reader to treat it as an RNG.
|
||||
- Imported documentation from `std::rand`
|
||||
- Imported tests from `std::rand`
|
||||
|
||||
|
||||
## [0.1.1] - 2015-02-03
|
||||
### Added
|
||||
- Migrate to a cargo-compatible directory structure.
|
||||
|
||||
### Fixed
|
||||
- Do not use entropy during `gen_weighted_bool(1)`
|
||||
|
||||
|
||||
## [Rust 0.12.0] - 2014-10-09
|
||||
### Added
|
||||
- Impl Rand for tuples of arity 11 and 12
|
||||
- Include ChaCha pseudorandom generator
|
||||
- Add `next_f64` and `next_f32` to Rng
|
||||
- Implement Clone for PRNGs
|
||||
|
||||
### Changed
|
||||
- Rename `TaskRng` to `ThreadRng` and `task_rng` to `thread_rng` (since a
|
||||
runtime is removed from Rust).
|
||||
|
||||
### Fixed
|
||||
- Improved performance of ISAAC and ISAAC64 by 30% and 12 % respectively, by
|
||||
informing the optimiser that indexing is never out-of-bounds.
|
||||
|
||||
### Removed
|
||||
- Removed the Deprecated `choose_option`
|
||||
|
||||
|
||||
## [Rust 0.11.0] - 2014-07-02
|
||||
### Added
|
||||
- document when to use `OSRng` in cryptographic context, and explain why we use `/dev/urandom` instead of `/dev/random`
|
||||
- `Rng::gen_iter()` which will return an infinite stream of random values
|
||||
- `Rng::gen_ascii_chars()` which will return an infinite stream of random ascii characters
|
||||
|
||||
### Changed
|
||||
- Now only depends on libcore! 2adf5363f88ffe06f6d2ea5c338d1b186d47f4a1
|
||||
- Remove `Rng.choose()`, rename `Rng.choose_option()` to `.choose()`
|
||||
- Rename OSRng to OsRng
|
||||
- The WeightedChoice structure is no longer built with a `Vec<Weighted<T>>`,
|
||||
but rather a `&mut [Weighted<T>]`. This means that the WeightedChoice
|
||||
structure now has a lifetime associated with it.
|
||||
- The `sample` method on `Rng` has been moved to a top-level function in the
|
||||
`rand` module due to its dependence on `Vec`.
|
||||
|
||||
### Removed
|
||||
- `Rng::gen_vec()` was removed. Previous behavior can be regained with
|
||||
`rng.gen_iter().take(n).collect()`
|
||||
- `Rng::gen_ascii_str()` was removed. Previous behavior can be regained with
|
||||
`rng.gen_ascii_chars().take(n).collect()`
|
||||
- {IsaacRng, Isaac64Rng, XorShiftRng}::new() have all been removed. These all
|
||||
relied on being able to use an OSRng for seeding, but this is no longer
|
||||
available in librand (where these types are defined). To retain the same
|
||||
functionality, these types now implement the `Rand` trait so they can be
|
||||
generated with a random seed from another random number generator. This allows
|
||||
the stdlib to use an OSRng to create seeded instances of these RNGs.
|
||||
- Rand implementations for `Box<T>` and `@T` were removed. These seemed to be
|
||||
pretty rare in the codebase, and it allows for librand to not depend on
|
||||
liballoc. Additionally, other pointer types like Rc<T> and Arc<T> were not
|
||||
supported.
|
||||
- Remove a slew of old deprecated functions
|
||||
|
||||
|
||||
## [Rust 0.10] - 2014-04-03
|
||||
### Changed
|
||||
- replace `Rng.shuffle's` functionality with `.shuffle_mut`
|
||||
- bubble up IO errors when creating an OSRng
|
||||
|
||||
### Fixed
|
||||
- Use `fill()` instead of `read()`
|
||||
- Rewrite OsRng in Rust for windows
|
||||
|
||||
## [0.10-pre] - 2014-03-02
|
||||
### Added
|
||||
- Seperate `rand` out of the standard library
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "rand"
|
||||
version = "0.4.3"
|
||||
authors = ["The Rust Project Developers"]
|
||||
description = "Random number generators and other randomness functionality.\n"
|
||||
homepage = "https://github.com/rust-lang-nursery/rand"
|
||||
documentation = "https://docs.rs/rand"
|
||||
readme = "README.md"
|
||||
keywords = ["random", "rng"]
|
||||
categories = ["algorithms"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rust-lang-nursery/rand"
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
default = ["std"]
|
||||
i128_support = []
|
||||
nightly = ["i128_support"]
|
||||
std = ["libc"]
|
||||
[target."cfg(target_os = \"fuchsia\")".dependencies.fuchsia-zircon]
|
||||
version = "0.3.2"
|
||||
[target."cfg(unix)".dependencies.libc]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = ["minwindef", "ntsecapi", "profileapi", "winnt"]
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2014 The Rust Project Developers
|
||||
|
||||
Permission 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.
|
|
@ -0,0 +1,139 @@
|
|||
rand
|
||||
====
|
||||
|
||||
A Rust library for random number generators and other randomness functionality.
|
||||
|
||||
[![Build Status](https://travis-ci.org/rust-lang-nursery/rand.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/rand)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/rm5c9o33k3jhchbw?svg=true)](https://ci.appveyor.com/project/alexcrichton/rand)
|
||||
|
||||
[Documentation](https://docs.rs/rand)
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
rand = "0.4"
|
||||
```
|
||||
|
||||
and this to your crate root:
|
||||
|
||||
```rust
|
||||
extern crate rand;
|
||||
```
|
||||
|
||||
### Versions
|
||||
|
||||
Version `0.4`was released in December 2017. It contains almost no breaking
|
||||
changes since the `0.3` series, but nevertheless contains some significant
|
||||
new code, including a new "external" entropy source (`JitterRng`) and `no_std`
|
||||
support.
|
||||
|
||||
Version `0.5` is in development and contains significant performance
|
||||
improvements for the ISAAC random number generators.
|
||||
|
||||
## Examples
|
||||
|
||||
There is built-in support for a random number generator (RNG) associated with each thread stored in thread-local storage. This RNG can be accessed via thread_rng, or used implicitly via random. This RNG is normally randomly seeded from an operating-system source of randomness, e.g. /dev/urandom on Unix systems, and will automatically reseed itself from this source after generating 32 KiB of random data.
|
||||
|
||||
```rust
|
||||
let tuple = rand::random::<(f64, char)>();
|
||||
println!("{:?}", tuple)
|
||||
```
|
||||
|
||||
```rust
|
||||
use rand::Rng;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
if rng.gen() { // random bool
|
||||
println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>())
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible to use other RNG types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default.
|
||||
|
||||
```rust
|
||||
use rand::{Rng, ChaChaRng};
|
||||
|
||||
let mut rng = rand::ChaChaRng::new_unseeded();
|
||||
println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>())
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
By default, `rand` is built with all stable features available. The following
|
||||
optional features are available:
|
||||
|
||||
- `i128_support` enables support for generating `u128` and `i128` values
|
||||
- `nightly` enables all unstable features (`i128_support`)
|
||||
- `std` enabled by default; by setting "default-features = false" `no_std`
|
||||
mode is activated; this removes features depending on `std` functionality:
|
||||
|
||||
- `OsRng` is entirely unavailable
|
||||
- `JitterRng` code is still present, but a nanosecond timer must be
|
||||
provided via `JitterRng::new_with_timer`
|
||||
- Since no external entropy is available, it is not possible to create
|
||||
generators with fresh seeds (user must provide entropy)
|
||||
- `thread_rng`, `weak_rng` and `random` are all disabled
|
||||
- exponential, normal and gamma type distributions are unavailable
|
||||
since `exp` and `log` functions are not provided in `core`
|
||||
- any code requiring `Vec` or `Box`
|
||||
- `alloc` can be used instead of `std` to provide `Vec` and `Box`
|
||||
|
||||
## Testing
|
||||
|
||||
Unfortunately, `cargo test` does not test everything. The following tests are
|
||||
recommended:
|
||||
|
||||
```
|
||||
# Basic tests for rand and sub-crates
|
||||
cargo test --all
|
||||
|
||||
# Test no_std support (build only since nearly all tests require std)
|
||||
cargo build --all --no-default-features
|
||||
|
||||
# Test 128-bit support (requires nightly)
|
||||
cargo test --all --features nightly
|
||||
|
||||
# Benchmarks (requires nightly)
|
||||
cargo bench
|
||||
# or just to test the benchmark code:
|
||||
cargo test --benches
|
||||
```
|
||||
|
||||
# `derive(Rand)`
|
||||
|
||||
You can derive the `Rand` trait for your custom type via the `#[derive(Rand)]`
|
||||
directive. To use this first add this to your Cargo.toml:
|
||||
|
||||
```toml
|
||||
rand = "0.4"
|
||||
rand_derive = "0.3"
|
||||
```
|
||||
|
||||
Next in your crate:
|
||||
|
||||
```rust
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate rand_derive;
|
||||
|
||||
#[derive(Rand, Debug)]
|
||||
struct MyStruct {
|
||||
a: i32,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", rand::random::<MyStruct>());
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# License
|
||||
|
||||
`rand` is primarily distributed under the terms of both the MIT
|
||||
license and the Apache License (Version 2.0).
|
||||
|
||||
See LICENSE-APACHE, and LICENSE-MIT for details.
|
|
@ -0,0 +1,133 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate rand;
|
||||
|
||||
const RAND_BENCH_N: u64 = 1000;
|
||||
const BYTES_LEN: usize = 1024;
|
||||
|
||||
use std::mem::size_of;
|
||||
use test::{black_box, Bencher};
|
||||
|
||||
use rand::{Rng, StdRng, OsRng, JitterRng};
|
||||
use rand::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng};
|
||||
|
||||
macro_rules! gen_bytes {
|
||||
($fnn:ident, $gen:ident) => {
|
||||
#[bench]
|
||||
fn $fnn(b: &mut Bencher) {
|
||||
let mut rng: $gen = OsRng::new().unwrap().gen();
|
||||
let mut buf = [0u8; BYTES_LEN];
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
rng.fill_bytes(&mut buf);
|
||||
black_box(buf);
|
||||
}
|
||||
});
|
||||
b.bytes = BYTES_LEN as u64 * RAND_BENCH_N;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gen_bytes_new {
|
||||
($fnn:ident, $gen:ident) => {
|
||||
#[bench]
|
||||
fn $fnn(b: &mut Bencher) {
|
||||
let mut rng = $gen::new().unwrap();
|
||||
let mut buf = [0u8; BYTES_LEN];
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
rng.fill_bytes(&mut buf);
|
||||
black_box(buf);
|
||||
}
|
||||
});
|
||||
b.bytes = BYTES_LEN as u64 * RAND_BENCH_N;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen_bytes!(gen_bytes_xorshift, XorShiftRng);
|
||||
gen_bytes!(gen_bytes_isaac, IsaacRng);
|
||||
gen_bytes!(gen_bytes_isaac64, Isaac64Rng);
|
||||
gen_bytes!(gen_bytes_chacha, ChaChaRng);
|
||||
gen_bytes_new!(gen_bytes_std, StdRng);
|
||||
gen_bytes_new!(gen_bytes_os, OsRng);
|
||||
|
||||
|
||||
macro_rules! gen_uint {
|
||||
($fnn:ident, $ty:ty, $gen:ident) => {
|
||||
#[bench]
|
||||
fn $fnn(b: &mut Bencher) {
|
||||
let mut rng: $gen = OsRng::new().unwrap().gen();
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
black_box(rng.gen::<$ty>());
|
||||
}
|
||||
});
|
||||
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gen_uint_new {
|
||||
($fnn:ident, $ty:ty, $gen:ident) => {
|
||||
#[bench]
|
||||
fn $fnn(b: &mut Bencher) {
|
||||
let mut rng = $gen::new().unwrap();
|
||||
b.iter(|| {
|
||||
for _ in 0..RAND_BENCH_N {
|
||||
black_box(rng.gen::<$ty>());
|
||||
}
|
||||
});
|
||||
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen_uint!(gen_u32_xorshift, u32, XorShiftRng);
|
||||
gen_uint!(gen_u32_isaac, u32, IsaacRng);
|
||||
gen_uint!(gen_u32_isaac64, u32, Isaac64Rng);
|
||||
gen_uint!(gen_u32_chacha, u32, ChaChaRng);
|
||||
gen_uint_new!(gen_u32_std, u32, StdRng);
|
||||
gen_uint_new!(gen_u32_os, u32, OsRng);
|
||||
|
||||
gen_uint!(gen_u64_xorshift, u64, XorShiftRng);
|
||||
gen_uint!(gen_u64_isaac, u64, IsaacRng);
|
||||
gen_uint!(gen_u64_isaac64, u64, Isaac64Rng);
|
||||
gen_uint!(gen_u64_chacha, u64, ChaChaRng);
|
||||
gen_uint_new!(gen_u64_std, u64, StdRng);
|
||||
gen_uint_new!(gen_u64_os, u64, OsRng);
|
||||
|
||||
#[bench]
|
||||
fn gen_u64_jitter(b: &mut Bencher) {
|
||||
let mut rng = JitterRng::new().unwrap();
|
||||
b.iter(|| {
|
||||
black_box(rng.gen::<u64>());
|
||||
});
|
||||
b.bytes = size_of::<u64>() as u64;
|
||||
}
|
||||
|
||||
macro_rules! init_gen {
|
||||
($fnn:ident, $gen:ident) => {
|
||||
#[bench]
|
||||
fn $fnn(b: &mut Bencher) {
|
||||
let mut rng: XorShiftRng = OsRng::new().unwrap().gen();
|
||||
b.iter(|| {
|
||||
let r2: $gen = rng.gen();
|
||||
black_box(r2);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_gen!(init_xorshift, XorShiftRng);
|
||||
init_gen!(init_isaac, IsaacRng);
|
||||
init_gen!(init_isaac64, Isaac64Rng);
|
||||
init_gen!(init_chacha, ChaChaRng);
|
||||
|
||||
#[bench]
|
||||
fn init_jitter(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(JitterRng::new().unwrap());
|
||||
});
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate rand;
|
||||
|
||||
use test::{black_box, Bencher};
|
||||
|
||||
use rand::{Rng, weak_rng};
|
||||
use rand::seq::*;
|
||||
|
||||
#[bench]
|
||||
fn misc_shuffle_100(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let x : &mut [usize] = &mut [1; 100];
|
||||
b.iter(|| {
|
||||
rng.shuffle(x);
|
||||
black_box(&x);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn misc_sample_iter_10_of_100(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let x : &[usize] = &[1; 100];
|
||||
b.iter(|| {
|
||||
black_box(sample_iter(&mut rng, x, 10).unwrap_or_else(|e| e));
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn misc_sample_slice_10_of_100(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let x : &[usize] = &[1; 100];
|
||||
b.iter(|| {
|
||||
black_box(sample_slice(&mut rng, x, 10));
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn misc_sample_slice_ref_10_of_100(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
let x : &[usize] = &[1; 100];
|
||||
b.iter(|| {
|
||||
black_box(sample_slice_ref(&mut rng, x, 10));
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! sample_indices {
|
||||
($name:ident, $amount:expr, $length:expr) => {
|
||||
#[bench]
|
||||
fn $name(b: &mut Bencher) {
|
||||
let mut rng = weak_rng();
|
||||
b.iter(|| {
|
||||
black_box(sample_indices(&mut rng, $length, $amount));
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sample_indices!(misc_sample_indices_10_of_1k, 10, 1000);
|
||||
sample_indices!(misc_sample_indices_50_of_1k, 50, 1000);
|
||||
sample_indices!(misc_sample_indices_100_of_1k, 100, 1000);
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The exponential distribution.
|
||||
|
||||
use {Rng, Rand};
|
||||
use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample};
|
||||
|
||||
/// A wrapper around an `f64` to generate Exp(1) random numbers.
|
||||
///
|
||||
/// See `Exp` for the general exponential distribution.
|
||||
///
|
||||
/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. The
|
||||
/// exact description in the paper was adjusted to use tables for the
|
||||
/// exponential distribution rather than normal.
|
||||
///
|
||||
/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
|
||||
/// Generate Normal Random
|
||||
/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
|
||||
/// College, Oxford
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::exponential::Exp1;
|
||||
///
|
||||
/// let Exp1(x) = rand::random();
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Exp1(pub f64);
|
||||
|
||||
// This could be done via `-rng.gen::<f64>().ln()` but that is slower.
|
||||
impl Rand for Exp1 {
|
||||
#[inline]
|
||||
fn rand<R:Rng>(rng: &mut R) -> Exp1 {
|
||||
#[inline]
|
||||
fn pdf(x: f64) -> f64 {
|
||||
(-x).exp()
|
||||
}
|
||||
#[inline]
|
||||
fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 {
|
||||
ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln()
|
||||
}
|
||||
|
||||
Exp1(ziggurat(rng, false,
|
||||
&ziggurat_tables::ZIG_EXP_X,
|
||||
&ziggurat_tables::ZIG_EXP_F,
|
||||
pdf, zero_case))
|
||||
}
|
||||
}
|
||||
|
||||
/// The exponential distribution `Exp(lambda)`.
|
||||
///
|
||||
/// This distribution has density function: `f(x) = lambda *
|
||||
/// exp(-lambda * x)` for `x > 0`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{Exp, IndependentSample};
|
||||
///
|
||||
/// let exp = Exp::new(2.0);
|
||||
/// let v = exp.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a Exp(2) distribution", v);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Exp {
|
||||
/// `lambda` stored as `1/lambda`, since this is what we scale by.
|
||||
lambda_inverse: f64
|
||||
}
|
||||
|
||||
impl Exp {
|
||||
/// Construct a new `Exp` with the given shape parameter
|
||||
/// `lambda`. Panics if `lambda <= 0`.
|
||||
#[inline]
|
||||
pub fn new(lambda: f64) -> Exp {
|
||||
assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0");
|
||||
Exp { lambda_inverse: 1.0 / lambda }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sample<f64> for Exp {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for Exp {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
let Exp1(n) = rng.gen::<Exp1>();
|
||||
n * self.lambda_inverse
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use distributions::{Sample, IndependentSample};
|
||||
use super::Exp;
|
||||
|
||||
#[test]
|
||||
fn test_exp() {
|
||||
let mut exp = Exp::new(10.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
assert!(exp.sample(&mut rng) >= 0.0);
|
||||
assert!(exp.ind_sample(&mut rng) >= 0.0);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_exp_invalid_lambda_zero() {
|
||||
Exp::new(0.0);
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_exp_invalid_lambda_neg() {
|
||||
Exp::new(-10.0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,386 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// ignore-lexer-test FIXME #15679
|
||||
|
||||
//! The Gamma and derived distributions.
|
||||
|
||||
use self::GammaRepr::*;
|
||||
use self::ChiSquaredRepr::*;
|
||||
|
||||
use {Rng, Open01};
|
||||
use super::normal::StandardNormal;
|
||||
use super::{IndependentSample, Sample, Exp};
|
||||
|
||||
/// The Gamma distribution `Gamma(shape, scale)` distribution.
|
||||
///
|
||||
/// The density function of this distribution is
|
||||
///
|
||||
/// ```text
|
||||
/// f(x) = x^(k - 1) * exp(-x / θ) / (Γ(k) * θ^k)
|
||||
/// ```
|
||||
///
|
||||
/// where `Γ` is the Gamma function, `k` is the shape and `θ` is the
|
||||
/// scale and both `k` and `θ` are strictly positive.
|
||||
///
|
||||
/// The algorithm used is that described by Marsaglia & Tsang 2000[1],
|
||||
/// falling back to directly sampling from an Exponential for `shape
|
||||
/// == 1`, and using the boosting technique described in [1] for
|
||||
/// `shape < 1`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{IndependentSample, Gamma};
|
||||
///
|
||||
/// let gamma = Gamma::new(2.0, 5.0);
|
||||
/// let v = gamma.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a Gamma(2, 5) distribution", v);
|
||||
/// ```
|
||||
///
|
||||
/// [1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method
|
||||
/// for Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3
|
||||
/// (September 2000),
|
||||
/// 363-372. DOI:[10.1145/358407.358414](http://doi.acm.org/10.1145/358407.358414)
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Gamma {
|
||||
repr: GammaRepr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum GammaRepr {
|
||||
Large(GammaLargeShape),
|
||||
One(Exp),
|
||||
Small(GammaSmallShape)
|
||||
}
|
||||
|
||||
// These two helpers could be made public, but saving the
|
||||
// match-on-Gamma-enum branch from using them directly (e.g. if one
|
||||
// knows that the shape is always > 1) doesn't appear to be much
|
||||
// faster.
|
||||
|
||||
/// Gamma distribution where the shape parameter is less than 1.
|
||||
///
|
||||
/// Note, samples from this require a compulsory floating-point `pow`
|
||||
/// call, which makes it significantly slower than sampling from a
|
||||
/// gamma distribution where the shape parameter is greater than or
|
||||
/// equal to 1.
|
||||
///
|
||||
/// See `Gamma` for sampling from a Gamma distribution with general
|
||||
/// shape parameters.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct GammaSmallShape {
|
||||
inv_shape: f64,
|
||||
large_shape: GammaLargeShape
|
||||
}
|
||||
|
||||
/// Gamma distribution where the shape parameter is larger than 1.
|
||||
///
|
||||
/// See `Gamma` for sampling from a Gamma distribution with general
|
||||
/// shape parameters.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct GammaLargeShape {
|
||||
scale: f64,
|
||||
c: f64,
|
||||
d: f64
|
||||
}
|
||||
|
||||
impl Gamma {
|
||||
/// Construct an object representing the `Gamma(shape, scale)`
|
||||
/// distribution.
|
||||
///
|
||||
/// Panics if `shape <= 0` or `scale <= 0`.
|
||||
#[inline]
|
||||
pub fn new(shape: f64, scale: f64) -> Gamma {
|
||||
assert!(shape > 0.0, "Gamma::new called with shape <= 0");
|
||||
assert!(scale > 0.0, "Gamma::new called with scale <= 0");
|
||||
|
||||
let repr = if shape == 1.0 {
|
||||
One(Exp::new(1.0 / scale))
|
||||
} else if shape < 1.0 {
|
||||
Small(GammaSmallShape::new_raw(shape, scale))
|
||||
} else {
|
||||
Large(GammaLargeShape::new_raw(shape, scale))
|
||||
};
|
||||
Gamma { repr: repr }
|
||||
}
|
||||
}
|
||||
|
||||
impl GammaSmallShape {
|
||||
fn new_raw(shape: f64, scale: f64) -> GammaSmallShape {
|
||||
GammaSmallShape {
|
||||
inv_shape: 1. / shape,
|
||||
large_shape: GammaLargeShape::new_raw(shape + 1.0, scale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GammaLargeShape {
|
||||
fn new_raw(shape: f64, scale: f64) -> GammaLargeShape {
|
||||
let d = shape - 1. / 3.;
|
||||
GammaLargeShape {
|
||||
scale: scale,
|
||||
c: 1. / (9. * d).sqrt(),
|
||||
d: d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sample<f64> for Gamma {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl Sample<f64> for GammaSmallShape {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl Sample<f64> for GammaLargeShape {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
|
||||
impl IndependentSample<f64> for Gamma {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
match self.repr {
|
||||
Small(ref g) => g.ind_sample(rng),
|
||||
One(ref g) => g.ind_sample(rng),
|
||||
Large(ref g) => g.ind_sample(rng),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IndependentSample<f64> for GammaSmallShape {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
let Open01(u) = rng.gen::<Open01<f64>>();
|
||||
|
||||
self.large_shape.ind_sample(rng) * u.powf(self.inv_shape)
|
||||
}
|
||||
}
|
||||
impl IndependentSample<f64> for GammaLargeShape {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
loop {
|
||||
let StandardNormal(x) = rng.gen::<StandardNormal>();
|
||||
let v_cbrt = 1.0 + self.c * x;
|
||||
if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0
|
||||
continue
|
||||
}
|
||||
|
||||
let v = v_cbrt * v_cbrt * v_cbrt;
|
||||
let Open01(u) = rng.gen::<Open01<f64>>();
|
||||
|
||||
let x_sqr = x * x;
|
||||
if u < 1.0 - 0.0331 * x_sqr * x_sqr ||
|
||||
u.ln() < 0.5 * x_sqr + self.d * (1.0 - v + v.ln()) {
|
||||
return self.d * v * self.scale
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of
|
||||
/// freedom.
|
||||
///
|
||||
/// For `k > 0` integral, this distribution is the sum of the squares
|
||||
/// of `k` independent standard normal random variables. For other
|
||||
/// `k`, this uses the equivalent characterisation
|
||||
/// `χ²(k) = Gamma(k/2, 2)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{ChiSquared, IndependentSample};
|
||||
///
|
||||
/// let chi = ChiSquared::new(11.0);
|
||||
/// let v = chi.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a χ²(11) distribution", v)
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ChiSquared {
|
||||
repr: ChiSquaredRepr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum ChiSquaredRepr {
|
||||
// k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1,
|
||||
// e.g. when alpha = 1/2 as it would be for this case, so special-
|
||||
// casing and using the definition of N(0,1)^2 is faster.
|
||||
DoFExactlyOne,
|
||||
DoFAnythingElse(Gamma),
|
||||
}
|
||||
|
||||
impl ChiSquared {
|
||||
/// Create a new chi-squared distribution with degrees-of-freedom
|
||||
/// `k`. Panics if `k < 0`.
|
||||
pub fn new(k: f64) -> ChiSquared {
|
||||
let repr = if k == 1.0 {
|
||||
DoFExactlyOne
|
||||
} else {
|
||||
assert!(k > 0.0, "ChiSquared::new called with `k` < 0");
|
||||
DoFAnythingElse(Gamma::new(0.5 * k, 2.0))
|
||||
};
|
||||
ChiSquared { repr: repr }
|
||||
}
|
||||
}
|
||||
impl Sample<f64> for ChiSquared {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for ChiSquared {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
match self.repr {
|
||||
DoFExactlyOne => {
|
||||
// k == 1 => N(0,1)^2
|
||||
let StandardNormal(norm) = rng.gen::<StandardNormal>();
|
||||
norm * norm
|
||||
}
|
||||
DoFAnythingElse(ref g) => g.ind_sample(rng)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The Fisher F distribution `F(m, n)`.
|
||||
///
|
||||
/// This distribution is equivalent to the ratio of two normalised
|
||||
/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) /
|
||||
/// (χ²(n)/n)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{FisherF, IndependentSample};
|
||||
///
|
||||
/// let f = FisherF::new(2.0, 32.0);
|
||||
/// let v = f.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from an F(2, 32) distribution", v)
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FisherF {
|
||||
numer: ChiSquared,
|
||||
denom: ChiSquared,
|
||||
// denom_dof / numer_dof so that this can just be a straight
|
||||
// multiplication, rather than a division.
|
||||
dof_ratio: f64,
|
||||
}
|
||||
|
||||
impl FisherF {
|
||||
/// Create a new `FisherF` distribution, with the given
|
||||
/// parameter. Panics if either `m` or `n` are not positive.
|
||||
pub fn new(m: f64, n: f64) -> FisherF {
|
||||
assert!(m > 0.0, "FisherF::new called with `m < 0`");
|
||||
assert!(n > 0.0, "FisherF::new called with `n < 0`");
|
||||
|
||||
FisherF {
|
||||
numer: ChiSquared::new(m),
|
||||
denom: ChiSquared::new(n),
|
||||
dof_ratio: n / m
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sample<f64> for FisherF {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for FisherF {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio
|
||||
}
|
||||
}
|
||||
|
||||
/// The Student t distribution, `t(nu)`, where `nu` is the degrees of
|
||||
/// freedom.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{StudentT, IndependentSample};
|
||||
///
|
||||
/// let t = StudentT::new(11.0);
|
||||
/// let v = t.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a t(11) distribution", v)
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct StudentT {
|
||||
chi: ChiSquared,
|
||||
dof: f64
|
||||
}
|
||||
|
||||
impl StudentT {
|
||||
/// Create a new Student t distribution with `n` degrees of
|
||||
/// freedom. Panics if `n <= 0`.
|
||||
pub fn new(n: f64) -> StudentT {
|
||||
assert!(n > 0.0, "StudentT::new called with `n <= 0`");
|
||||
StudentT {
|
||||
chi: ChiSquared::new(n),
|
||||
dof: n
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sample<f64> for StudentT {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for StudentT {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
let StandardNormal(norm) = rng.gen::<StandardNormal>();
|
||||
norm * (self.dof / self.chi.ind_sample(rng)).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use distributions::{Sample, IndependentSample};
|
||||
use super::{ChiSquared, StudentT, FisherF};
|
||||
|
||||
#[test]
|
||||
fn test_chi_squared_one() {
|
||||
let mut chi = ChiSquared::new(1.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
chi.sample(&mut rng);
|
||||
chi.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_chi_squared_small() {
|
||||
let mut chi = ChiSquared::new(0.5);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
chi.sample(&mut rng);
|
||||
chi.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_chi_squared_large() {
|
||||
let mut chi = ChiSquared::new(30.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
chi.sample(&mut rng);
|
||||
chi.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_chi_squared_invalid_dof() {
|
||||
ChiSquared::new(-1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_f() {
|
||||
let mut f = FisherF::new(2.0, 32.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
f.sample(&mut rng);
|
||||
f.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_t() {
|
||||
let mut t = StudentT::new(11.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
t.sample(&mut rng);
|
||||
t.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,409 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Sampling from random distributions.
|
||||
//!
|
||||
//! This is a generalization of `Rand` to allow parameters to control the
|
||||
//! exact properties of the generated values, e.g. the mean and standard
|
||||
//! deviation of a normal distribution. The `Sample` trait is the most
|
||||
//! general, and allows for generating values that change some state
|
||||
//! internally. The `IndependentSample` trait is for generating values
|
||||
//! that do not need to record state.
|
||||
|
||||
use core::marker;
|
||||
|
||||
use {Rng, Rand};
|
||||
|
||||
pub use self::range::Range;
|
||||
#[cfg(feature="std")]
|
||||
pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT};
|
||||
#[cfg(feature="std")]
|
||||
pub use self::normal::{Normal, LogNormal};
|
||||
#[cfg(feature="std")]
|
||||
pub use self::exponential::Exp;
|
||||
|
||||
pub mod range;
|
||||
#[cfg(feature="std")]
|
||||
pub mod gamma;
|
||||
#[cfg(feature="std")]
|
||||
pub mod normal;
|
||||
#[cfg(feature="std")]
|
||||
pub mod exponential;
|
||||
|
||||
#[cfg(feature="std")]
|
||||
mod ziggurat_tables;
|
||||
|
||||
/// Types that can be used to create a random instance of `Support`.
|
||||
pub trait Sample<Support> {
|
||||
/// Generate a random value of `Support`, using `rng` as the
|
||||
/// source of randomness.
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> Support;
|
||||
}
|
||||
|
||||
/// `Sample`s that do not require keeping track of state.
|
||||
///
|
||||
/// Since no state is recorded, each sample is (statistically)
|
||||
/// independent of all others, assuming the `Rng` used has this
|
||||
/// property.
|
||||
// FIXME maybe having this separate is overkill (the only reason is to
|
||||
// take &self rather than &mut self)? or maybe this should be the
|
||||
// trait called `Sample` and the other should be `DependentSample`.
|
||||
pub trait IndependentSample<Support>: Sample<Support> {
|
||||
/// Generate a random value.
|
||||
fn ind_sample<R: Rng>(&self, &mut R) -> Support;
|
||||
}
|
||||
|
||||
/// A wrapper for generating types that implement `Rand` via the
|
||||
/// `Sample` & `IndependentSample` traits.
|
||||
#[derive(Debug)]
|
||||
pub struct RandSample<Sup> {
|
||||
_marker: marker::PhantomData<fn() -> Sup>,
|
||||
}
|
||||
|
||||
impl<Sup> Copy for RandSample<Sup> {}
|
||||
impl<Sup> Clone for RandSample<Sup> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
impl<Sup: Rand> Sample<Sup> for RandSample<Sup> {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) }
|
||||
}
|
||||
|
||||
impl<Sup: Rand> IndependentSample<Sup> for RandSample<Sup> {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> Sup {
|
||||
rng.gen()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Sup> RandSample<Sup> {
|
||||
pub fn new() -> RandSample<Sup> {
|
||||
RandSample { _marker: marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
/// A value with a particular weight for use with `WeightedChoice`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Weighted<T> {
|
||||
/// The numerical weight of this item
|
||||
pub weight: u32,
|
||||
/// The actual item which is being weighted
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
/// A distribution that selects from a finite collection of weighted items.
|
||||
///
|
||||
/// Each item has an associated weight that influences how likely it
|
||||
/// is to be chosen: higher weight is more likely.
|
||||
///
|
||||
/// The `Clone` restriction is a limitation of the `Sample` and
|
||||
/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for
|
||||
/// all `T`, as is `u32`, so one can store references or indices into
|
||||
/// another vector.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{Weighted, WeightedChoice, IndependentSample};
|
||||
///
|
||||
/// let mut items = vec!(Weighted { weight: 2, item: 'a' },
|
||||
/// Weighted { weight: 4, item: 'b' },
|
||||
/// Weighted { weight: 1, item: 'c' });
|
||||
/// let wc = WeightedChoice::new(&mut items);
|
||||
/// let mut rng = rand::thread_rng();
|
||||
/// for _ in 0..16 {
|
||||
/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice.
|
||||
/// println!("{}", wc.ind_sample(&mut rng));
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct WeightedChoice<'a, T:'a> {
|
||||
items: &'a mut [Weighted<T>],
|
||||
weight_range: Range<u32>
|
||||
}
|
||||
|
||||
impl<'a, T: Clone> WeightedChoice<'a, T> {
|
||||
/// Create a new `WeightedChoice`.
|
||||
///
|
||||
/// Panics if:
|
||||
///
|
||||
/// - `items` is empty
|
||||
/// - the total weight is 0
|
||||
/// - the total weight is larger than a `u32` can contain.
|
||||
pub fn new(items: &'a mut [Weighted<T>]) -> WeightedChoice<'a, T> {
|
||||
// strictly speaking, this is subsumed by the total weight == 0 case
|
||||
assert!(!items.is_empty(), "WeightedChoice::new called with no items");
|
||||
|
||||
let mut running_total: u32 = 0;
|
||||
|
||||
// we convert the list from individual weights to cumulative
|
||||
// weights so we can binary search. This *could* drop elements
|
||||
// with weight == 0 as an optimisation.
|
||||
for item in items.iter_mut() {
|
||||
running_total = match running_total.checked_add(item.weight) {
|
||||
Some(n) => n,
|
||||
None => panic!("WeightedChoice::new called with a total weight \
|
||||
larger than a u32 can contain")
|
||||
};
|
||||
|
||||
item.weight = running_total;
|
||||
}
|
||||
assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0");
|
||||
|
||||
WeightedChoice {
|
||||
items: items,
|
||||
// we're likely to be generating numbers in this range
|
||||
// relatively often, so might as well cache it
|
||||
weight_range: Range::new(0, running_total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone> Sample<T> for WeightedChoice<'a, T> {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> T { self.ind_sample(rng) }
|
||||
}
|
||||
|
||||
impl<'a, T: Clone> IndependentSample<T> for WeightedChoice<'a, T> {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> T {
|
||||
// we want to find the first element that has cumulative
|
||||
// weight > sample_weight, which we do by binary since the
|
||||
// cumulative weights of self.items are sorted.
|
||||
|
||||
// choose a weight in [0, total_weight)
|
||||
let sample_weight = self.weight_range.ind_sample(rng);
|
||||
|
||||
// short circuit when it's the first item
|
||||
if sample_weight < self.items[0].weight {
|
||||
return self.items[0].item.clone();
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
let mut modifier = self.items.len();
|
||||
|
||||
// now we know that every possibility has an element to the
|
||||
// left, so we can just search for the last element that has
|
||||
// cumulative weight <= sample_weight, then the next one will
|
||||
// be "it". (Note that this greatest element will never be the
|
||||
// last element of the vector, since sample_weight is chosen
|
||||
// in [0, total_weight) and the cumulative weight of the last
|
||||
// one is exactly the total weight.)
|
||||
while modifier > 1 {
|
||||
let i = idx + modifier / 2;
|
||||
if self.items[i].weight <= sample_weight {
|
||||
// we're small, so look to the right, but allow this
|
||||
// exact element still.
|
||||
idx = i;
|
||||
// we need the `/ 2` to round up otherwise we'll drop
|
||||
// the trailing elements when `modifier` is odd.
|
||||
modifier += 1;
|
||||
} else {
|
||||
// otherwise we're too big, so go left. (i.e. do
|
||||
// nothing)
|
||||
}
|
||||
modifier /= 2;
|
||||
}
|
||||
return self.items[idx + 1].item.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample a random number using the Ziggurat method (specifically the
|
||||
/// ZIGNOR variant from Doornik 2005). Most of the arguments are
|
||||
/// directly from the paper:
|
||||
///
|
||||
/// * `rng`: source of randomness
|
||||
/// * `symmetric`: whether this is a symmetric distribution, or one-sided with P(x < 0) = 0.
|
||||
/// * `X`: the $x_i$ abscissae.
|
||||
/// * `F`: precomputed values of the PDF at the $x_i$, (i.e. $f(x_i)$)
|
||||
/// * `F_DIFF`: precomputed values of $f(x_i) - f(x_{i+1})$
|
||||
/// * `pdf`: the probability density function
|
||||
/// * `zero_case`: manual sampling from the tail when we chose the
|
||||
/// bottom box (i.e. i == 0)
|
||||
|
||||
// the perf improvement (25-50%) is definitely worth the extra code
|
||||
// size from force-inlining.
|
||||
#[cfg(feature="std")]
|
||||
#[inline(always)]
|
||||
fn ziggurat<R: Rng, P, Z>(
|
||||
rng: &mut R,
|
||||
symmetric: bool,
|
||||
x_tab: ziggurat_tables::ZigTable,
|
||||
f_tab: ziggurat_tables::ZigTable,
|
||||
mut pdf: P,
|
||||
mut zero_case: Z)
|
||||
-> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 {
|
||||
const SCALE: f64 = (1u64 << 53) as f64;
|
||||
loop {
|
||||
// reimplement the f64 generation as an optimisation suggested
|
||||
// by the Doornik paper: we have a lot of precision-space
|
||||
// (i.e. there are 11 bits of the 64 of a u64 to use after
|
||||
// creating a f64), so we might as well reuse some to save
|
||||
// generating a whole extra random number. (Seems to be 15%
|
||||
// faster.)
|
||||
//
|
||||
// This unfortunately misses out on the benefits of direct
|
||||
// floating point generation if an RNG like dSMFT is
|
||||
// used. (That is, such RNGs create floats directly, highly
|
||||
// efficiently and overload next_f32/f64, so by not calling it
|
||||
// this may be slower than it would be otherwise.)
|
||||
// FIXME: investigate/optimise for the above.
|
||||
let bits: u64 = rng.gen();
|
||||
let i = (bits & 0xff) as usize;
|
||||
let f = (bits >> 11) as f64 / SCALE;
|
||||
|
||||
// u is either U(-1, 1) or U(0, 1) depending on if this is a
|
||||
// symmetric distribution or not.
|
||||
let u = if symmetric {2.0 * f - 1.0} else {f};
|
||||
let x = u * x_tab[i];
|
||||
|
||||
let test_x = if symmetric { x.abs() } else {x};
|
||||
|
||||
// algebraically equivalent to |u| < x_tab[i+1]/x_tab[i] (or u < x_tab[i+1]/x_tab[i])
|
||||
if test_x < x_tab[i + 1] {
|
||||
return x;
|
||||
}
|
||||
if i == 0 {
|
||||
return zero_case(rng, u);
|
||||
}
|
||||
// algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1
|
||||
if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::<f64>() < pdf(x) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use {Rng, Rand};
|
||||
use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct ConstRand(usize);
|
||||
impl Rand for ConstRand {
|
||||
fn rand<R: Rng>(_: &mut R) -> ConstRand {
|
||||
ConstRand(0)
|
||||
}
|
||||
}
|
||||
|
||||
// 0, 1, 2, 3, ...
|
||||
struct CountingRng { i: u32 }
|
||||
impl Rng for CountingRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.i += 1;
|
||||
self.i - 1
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.next_u32() as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rand_sample() {
|
||||
let mut rand_sample = RandSample::<ConstRand>::new();
|
||||
|
||||
assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0));
|
||||
assert_eq!(rand_sample.ind_sample(&mut ::test::rng()), ConstRand(0));
|
||||
}
|
||||
#[test]
|
||||
fn test_weighted_choice() {
|
||||
// this makes assumptions about the internal implementation of
|
||||
// WeightedChoice, specifically: it doesn't reorder the items,
|
||||
// it doesn't do weird things to the RNG (so 0 maps to 0, 1 to
|
||||
// 1, internally; modulo a modulo operation).
|
||||
|
||||
macro_rules! t {
|
||||
($items:expr, $expected:expr) => {{
|
||||
let mut items = $items;
|
||||
let wc = WeightedChoice::new(&mut items);
|
||||
let expected = $expected;
|
||||
|
||||
let mut rng = CountingRng { i: 0 };
|
||||
|
||||
for &val in expected.iter() {
|
||||
assert_eq!(wc.ind_sample(&mut rng), val)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
t!(vec!(Weighted { weight: 1, item: 10}), [10]);
|
||||
|
||||
// skip some
|
||||
t!(vec!(Weighted { weight: 0, item: 20},
|
||||
Weighted { weight: 2, item: 21},
|
||||
Weighted { weight: 0, item: 22},
|
||||
Weighted { weight: 1, item: 23}),
|
||||
[21,21, 23]);
|
||||
|
||||
// different weights
|
||||
t!(vec!(Weighted { weight: 4, item: 30},
|
||||
Weighted { weight: 3, item: 31}),
|
||||
[30,30,30,30, 31,31,31]);
|
||||
|
||||
// check that we're binary searching
|
||||
// correctly with some vectors of odd
|
||||
// length.
|
||||
t!(vec!(Weighted { weight: 1, item: 40},
|
||||
Weighted { weight: 1, item: 41},
|
||||
Weighted { weight: 1, item: 42},
|
||||
Weighted { weight: 1, item: 43},
|
||||
Weighted { weight: 1, item: 44}),
|
||||
[40, 41, 42, 43, 44]);
|
||||
t!(vec!(Weighted { weight: 1, item: 50},
|
||||
Weighted { weight: 1, item: 51},
|
||||
Weighted { weight: 1, item: 52},
|
||||
Weighted { weight: 1, item: 53},
|
||||
Weighted { weight: 1, item: 54},
|
||||
Weighted { weight: 1, item: 55},
|
||||
Weighted { weight: 1, item: 56}),
|
||||
[50, 51, 52, 53, 54, 55, 56]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weighted_clone_initialization() {
|
||||
let initial : Weighted<u32> = Weighted {weight: 1, item: 1};
|
||||
let clone = initial.clone();
|
||||
assert_eq!(initial.weight, clone.weight);
|
||||
assert_eq!(initial.item, clone.item);
|
||||
}
|
||||
|
||||
#[test] #[should_panic]
|
||||
fn test_weighted_clone_change_weight() {
|
||||
let initial : Weighted<u32> = Weighted {weight: 1, item: 1};
|
||||
let mut clone = initial.clone();
|
||||
clone.weight = 5;
|
||||
assert_eq!(initial.weight, clone.weight);
|
||||
}
|
||||
|
||||
#[test] #[should_panic]
|
||||
fn test_weighted_clone_change_item() {
|
||||
let initial : Weighted<u32> = Weighted {weight: 1, item: 1};
|
||||
let mut clone = initial.clone();
|
||||
clone.item = 5;
|
||||
assert_eq!(initial.item, clone.item);
|
||||
|
||||
}
|
||||
|
||||
#[test] #[should_panic]
|
||||
fn test_weighted_choice_no_items() {
|
||||
WeightedChoice::<isize>::new(&mut []);
|
||||
}
|
||||
#[test] #[should_panic]
|
||||
fn test_weighted_choice_zero_weight() {
|
||||
WeightedChoice::new(&mut [Weighted { weight: 0, item: 0},
|
||||
Weighted { weight: 0, item: 1}]);
|
||||
}
|
||||
#[test] #[should_panic]
|
||||
fn test_weighted_choice_weight_overflows() {
|
||||
let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow
|
||||
WeightedChoice::new(&mut [Weighted { weight: x, item: 0 },
|
||||
Weighted { weight: 1, item: 1 },
|
||||
Weighted { weight: x, item: 2 },
|
||||
Weighted { weight: 1, item: 3 }]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The normal and derived distributions.
|
||||
|
||||
use {Rng, Rand, Open01};
|
||||
use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample};
|
||||
|
||||
/// A wrapper around an `f64` to generate N(0, 1) random numbers
|
||||
/// (a.k.a. a standard normal, or Gaussian).
|
||||
///
|
||||
/// See `Normal` for the general normal distribution.
|
||||
///
|
||||
/// Implemented via the ZIGNOR variant[1] of the Ziggurat method.
|
||||
///
|
||||
/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
|
||||
/// Generate Normal Random
|
||||
/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
|
||||
/// College, Oxford
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::normal::StandardNormal;
|
||||
///
|
||||
/// let StandardNormal(x) = rand::random();
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct StandardNormal(pub f64);
|
||||
|
||||
impl Rand for StandardNormal {
|
||||
fn rand<R:Rng>(rng: &mut R) -> StandardNormal {
|
||||
#[inline]
|
||||
fn pdf(x: f64) -> f64 {
|
||||
(-x*x/2.0).exp()
|
||||
}
|
||||
#[inline]
|
||||
fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
|
||||
// compute a random number in the tail by hand
|
||||
|
||||
// strange initial conditions, because the loop is not
|
||||
// do-while, so the condition should be true on the first
|
||||
// run, they get overwritten anyway (0 < 1, so these are
|
||||
// good).
|
||||
let mut x = 1.0f64;
|
||||
let mut y = 0.0f64;
|
||||
|
||||
while -2.0 * y < x * x {
|
||||
let Open01(x_) = rng.gen::<Open01<f64>>();
|
||||
let Open01(y_) = rng.gen::<Open01<f64>>();
|
||||
|
||||
x = x_.ln() / ziggurat_tables::ZIG_NORM_R;
|
||||
y = y_.ln();
|
||||
}
|
||||
|
||||
if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }
|
||||
}
|
||||
|
||||
StandardNormal(ziggurat(
|
||||
rng,
|
||||
true, // this is symmetric
|
||||
&ziggurat_tables::ZIG_NORM_X,
|
||||
&ziggurat_tables::ZIG_NORM_F,
|
||||
pdf, zero_case))
|
||||
}
|
||||
}
|
||||
|
||||
/// The normal distribution `N(mean, std_dev**2)`.
|
||||
///
|
||||
/// This uses the ZIGNOR variant of the Ziggurat method, see
|
||||
/// `StandardNormal` for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{Normal, IndependentSample};
|
||||
///
|
||||
/// // mean 2, standard deviation 3
|
||||
/// let normal = Normal::new(2.0, 3.0);
|
||||
/// let v = normal.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from a N(2, 9) distribution", v)
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Normal {
|
||||
mean: f64,
|
||||
std_dev: f64,
|
||||
}
|
||||
|
||||
impl Normal {
|
||||
/// Construct a new `Normal` distribution with the given mean and
|
||||
/// standard deviation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `std_dev < 0`.
|
||||
#[inline]
|
||||
pub fn new(mean: f64, std_dev: f64) -> Normal {
|
||||
assert!(std_dev >= 0.0, "Normal::new called with `std_dev` < 0");
|
||||
Normal {
|
||||
mean: mean,
|
||||
std_dev: std_dev
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sample<f64> for Normal {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for Normal {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
let StandardNormal(n) = rng.gen::<StandardNormal>();
|
||||
self.mean + self.std_dev * n
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The log-normal distribution `ln N(mean, std_dev**2)`.
|
||||
///
|
||||
/// If `X` is log-normal distributed, then `ln(X)` is `N(mean,
|
||||
/// std_dev**2)` distributed.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rand::distributions::{LogNormal, IndependentSample};
|
||||
///
|
||||
/// // mean 2, standard deviation 3
|
||||
/// let log_normal = LogNormal::new(2.0, 3.0);
|
||||
/// let v = log_normal.ind_sample(&mut rand::thread_rng());
|
||||
/// println!("{} is from an ln N(2, 9) distribution", v)
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LogNormal {
|
||||
norm: Normal
|
||||
}
|
||||
|
||||
impl LogNormal {
|
||||
/// Construct a new `LogNormal` distribution with the given mean
|
||||
/// and standard deviation.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `std_dev < 0`.
|
||||
#[inline]
|
||||
pub fn new(mean: f64, std_dev: f64) -> LogNormal {
|
||||
assert!(std_dev >= 0.0, "LogNormal::new called with `std_dev` < 0");
|
||||
LogNormal { norm: Normal::new(mean, std_dev) }
|
||||
}
|
||||
}
|
||||
impl Sample<f64> for LogNormal {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for LogNormal {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
self.norm.ind_sample(rng).exp()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use distributions::{Sample, IndependentSample};
|
||||
use super::{Normal, LogNormal};
|
||||
|
||||
#[test]
|
||||
fn test_normal() {
|
||||
let mut norm = Normal::new(10.0, 10.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
norm.sample(&mut rng);
|
||||
norm.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_normal_invalid_sd() {
|
||||
Normal::new(10.0, -1.0);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_log_normal() {
|
||||
let mut lnorm = LogNormal::new(10.0, 10.0);
|
||||
let mut rng = ::test::rng();
|
||||
for _ in 0..1000 {
|
||||
lnorm.sample(&mut rng);
|
||||
lnorm.ind_sample(&mut rng);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_log_normal_invalid_sd() {
|
||||
LogNormal::new(10.0, -1.0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Tables for distributions which are sampled using the ziggurat
|
||||
// algorithm. Autogenerated by `ziggurat_tables.py`.
|
||||
|
||||
pub type ZigTable = &'static [f64; 257];
|
||||
pub const ZIG_NORM_R: f64 = 3.654152885361008796;
|
||||
pub static ZIG_NORM_X: [f64; 257] =
|
||||
[3.910757959537090045, 3.654152885361008796, 3.449278298560964462, 3.320244733839166074,
|
||||
3.224575052047029100, 3.147889289517149969, 3.083526132001233044, 3.027837791768635434,
|
||||
2.978603279880844834, 2.934366867207854224, 2.894121053612348060, 2.857138730872132548,
|
||||
2.822877396825325125, 2.790921174000785765, 2.760944005278822555, 2.732685359042827056,
|
||||
2.705933656121858100, 2.680514643284522158, 2.656283037575502437, 2.633116393630324570,
|
||||
2.610910518487548515, 2.589575986706995181, 2.569035452680536569, 2.549221550323460761,
|
||||
2.530075232158516929, 2.511544441625342294, 2.493583041269680667, 2.476149939669143318,
|
||||
2.459208374333311298, 2.442725318198956774, 2.426670984935725972, 2.411018413899685520,
|
||||
2.395743119780480601, 2.380822795170626005, 2.366237056715818632, 2.351967227377659952,
|
||||
2.337996148795031370, 2.324308018869623016, 2.310888250599850036, 2.297723348901329565,
|
||||
2.284800802722946056, 2.272108990226823888, 2.259637095172217780, 2.247375032945807760,
|
||||
2.235313384928327984, 2.223443340090905718, 2.211756642882544366, 2.200245546609647995,
|
||||
2.188902771624720689, 2.177721467738641614, 2.166695180352645966, 2.155817819875063268,
|
||||
2.145083634046203613, 2.134487182844320152, 2.124023315687815661, 2.113687150684933957,
|
||||
2.103474055713146829, 2.093379631137050279, 2.083399693996551783, 2.073530263516978778,
|
||||
2.063767547809956415, 2.054107931648864849, 2.044547965215732788, 2.035084353727808715,
|
||||
2.025713947862032960, 2.016433734904371722, 2.007240830558684852, 1.998132471356564244,
|
||||
1.989106007615571325, 1.980158896898598364, 1.971288697931769640, 1.962493064942461896,
|
||||
1.953769742382734043, 1.945116560006753925, 1.936531428273758904, 1.928012334050718257,
|
||||
1.919557336591228847, 1.911164563769282232, 1.902832208548446369, 1.894558525668710081,
|
||||
1.886341828534776388, 1.878180486290977669, 1.870072921069236838, 1.862017605397632281,
|
||||
1.854013059758148119, 1.846057850283119750, 1.838150586580728607, 1.830289919680666566,
|
||||
1.822474540091783224, 1.814703175964167636, 1.806974591348693426, 1.799287584547580199,
|
||||
1.791640986550010028, 1.784033659547276329, 1.776464495522344977, 1.768932414909077933,
|
||||
1.761436365316706665, 1.753975320315455111, 1.746548278279492994, 1.739154261283669012,
|
||||
1.731792314050707216, 1.724461502945775715, 1.717160915015540690, 1.709889657069006086,
|
||||
1.702646854797613907, 1.695431651932238548, 1.688243209434858727, 1.681080704722823338,
|
||||
1.673943330923760353, 1.666830296159286684, 1.659740822855789499, 1.652674147080648526,
|
||||
1.645629517902360339, 1.638606196773111146, 1.631603456932422036, 1.624620582830568427,
|
||||
1.617656869570534228, 1.610711622367333673, 1.603784156023583041, 1.596873794420261339,
|
||||
1.589979870021648534, 1.583101723393471438, 1.576238702733332886, 1.569390163412534456,
|
||||
1.562555467528439657, 1.555733983466554893, 1.548925085471535512, 1.542128153226347553,
|
||||
1.535342571438843118, 1.528567729435024614, 1.521803020758293101, 1.515047842773992404,
|
||||
1.508301596278571965, 1.501563685112706548, 1.494833515777718391, 1.488110497054654369,
|
||||
1.481394039625375747, 1.474683555695025516, 1.467978458615230908, 1.461278162507407830,
|
||||
1.454582081885523293, 1.447889631277669675, 1.441200224845798017, 1.434513276002946425,
|
||||
1.427828197027290358, 1.421144398672323117, 1.414461289772464658, 1.407778276843371534,
|
||||
1.401094763676202559, 1.394410150925071257, 1.387723835686884621, 1.381035211072741964,
|
||||
1.374343665770030531, 1.367648583594317957, 1.360949343030101844, 1.354245316759430606,
|
||||
1.347535871177359290, 1.340820365893152122, 1.334098153216083604, 1.327368577624624679,
|
||||
1.320630975217730096, 1.313884673146868964, 1.307128989027353860, 1.300363230327433728,
|
||||
1.293586693733517645, 1.286798664489786415, 1.279998415710333237, 1.273185207661843732,
|
||||
1.266358287014688333, 1.259516886060144225, 1.252660221891297887, 1.245787495544997903,
|
||||
1.238897891102027415, 1.231990574742445110, 1.225064693752808020, 1.218119375481726552,
|
||||
1.211153726239911244, 1.204166830140560140, 1.197157747875585931, 1.190125515422801650,
|
||||
1.183069142678760732, 1.175987612011489825, 1.168879876726833800, 1.161744859441574240,
|
||||
1.154581450355851802, 1.147388505416733873, 1.140164844363995789, 1.132909248648336975,
|
||||
1.125620459211294389, 1.118297174115062909, 1.110938046009249502, 1.103541679420268151,
|
||||
1.096106627847603487, 1.088631390649514197, 1.081114409698889389, 1.073554065787871714,
|
||||
1.065948674757506653, 1.058296483326006454, 1.050595664586207123, 1.042844313139370538,
|
||||
1.035040439828605274, 1.027181966030751292, 1.019266717460529215, 1.011292417434978441,
|
||||
1.003256679539591412, 0.995156999629943084, 0.986990747093846266, 0.978755155288937750,
|
||||
0.970447311058864615, 0.962064143217605250, 0.953602409875572654, 0.945058684462571130,
|
||||
0.936429340280896860, 0.927710533396234771, 0.918898183643734989, 0.909987953490768997,
|
||||
0.900975224455174528, 0.891855070726792376, 0.882622229578910122, 0.873271068082494550,
|
||||
0.863795545546826915, 0.854189171001560554, 0.844444954902423661, 0.834555354079518752,
|
||||
0.824512208745288633, 0.814306670128064347, 0.803929116982664893, 0.793369058833152785,
|
||||
0.782615023299588763, 0.771654424216739354, 0.760473406422083165, 0.749056662009581653,
|
||||
0.737387211425838629, 0.725446140901303549, 0.713212285182022732, 0.700661841097584448,
|
||||
0.687767892786257717, 0.674499822827436479, 0.660822574234205984, 0.646695714884388928,
|
||||
0.632072236375024632, 0.616896989996235545, 0.601104617743940417, 0.584616766093722262,
|
||||
0.567338257040473026, 0.549151702313026790, 0.529909720646495108, 0.509423329585933393,
|
||||
0.487443966121754335, 0.463634336771763245, 0.437518402186662658, 0.408389134588000746,
|
||||
0.375121332850465727, 0.335737519180459465, 0.286174591747260509, 0.215241895913273806,
|
||||
0.000000000000000000];
|
||||
pub static ZIG_NORM_F: [f64; 257] =
|
||||
[0.000477467764586655, 0.001260285930498598, 0.002609072746106363, 0.004037972593371872,
|
||||
0.005522403299264754, 0.007050875471392110, 0.008616582769422917, 0.010214971439731100,
|
||||
0.011842757857943104, 0.013497450601780807, 0.015177088307982072, 0.016880083152595839,
|
||||
0.018605121275783350, 0.020351096230109354, 0.022117062707379922, 0.023902203305873237,
|
||||
0.025705804008632656, 0.027527235669693315, 0.029365939758230111, 0.031221417192023690,
|
||||
0.033093219458688698, 0.034980941461833073, 0.036884215688691151, 0.038802707404656918,
|
||||
0.040736110656078753, 0.042684144916619378, 0.044646552251446536, 0.046623094902089664,
|
||||
0.048613553216035145, 0.050617723861121788, 0.052635418276973649, 0.054666461325077916,
|
||||
0.056710690106399467, 0.058767952921137984, 0.060838108349751806, 0.062921024437977854,
|
||||
0.065016577971470438, 0.067124653828023989, 0.069245144397250269, 0.071377949059141965,
|
||||
0.073522973714240991, 0.075680130359194964, 0.077849336702372207, 0.080030515814947509,
|
||||
0.082223595813495684, 0.084428509570654661, 0.086645194450867782, 0.088873592068594229,
|
||||
0.091113648066700734, 0.093365311913026619, 0.095628536713353335, 0.097903279039215627,
|
||||
0.100189498769172020, 0.102487158942306270, 0.104796225622867056, 0.107116667775072880,
|
||||
0.109448457147210021, 0.111791568164245583, 0.114145977828255210, 0.116511665626037014,
|
||||
0.118888613443345698, 0.121276805485235437, 0.123676228202051403, 0.126086870220650349,
|
||||
0.128508722280473636, 0.130941777174128166, 0.133386029692162844, 0.135841476571757352,
|
||||
0.138308116449064322, 0.140785949814968309, 0.143274978974047118, 0.145775208006537926,
|
||||
0.148286642733128721, 0.150809290682410169, 0.153343161060837674, 0.155888264725064563,
|
||||
0.158444614156520225, 0.161012223438117663, 0.163591108232982951, 0.166181285765110071,
|
||||
0.168782774801850333, 0.171395595638155623, 0.174019770082499359, 0.176655321444406654,
|
||||
0.179302274523530397, 0.181960655600216487, 0.184630492427504539, 0.187311814224516926,
|
||||
0.190004651671193070, 0.192709036904328807, 0.195425003514885592, 0.198152586546538112,
|
||||
0.200891822495431333, 0.203642749311121501, 0.206405406398679298, 0.209179834621935651,
|
||||
0.211966076307852941, 0.214764175252008499, 0.217574176725178370, 0.220396127481011589,
|
||||
0.223230075764789593, 0.226076071323264877, 0.228934165415577484, 0.231804410825248525,
|
||||
0.234686861873252689, 0.237581574432173676, 0.240488605941449107, 0.243408015423711988,
|
||||
0.246339863502238771, 0.249284212419516704, 0.252241126056943765, 0.255210669955677150,
|
||||
0.258192911338648023, 0.261187919133763713, 0.264195763998317568, 0.267216518344631837,
|
||||
0.270250256366959984, 0.273297054069675804, 0.276356989296781264, 0.279430141762765316,
|
||||
0.282516593084849388, 0.285616426816658109, 0.288729728483353931, 0.291856585618280984,
|
||||
0.294997087801162572, 0.298151326697901342, 0.301319396102034120, 0.304501391977896274,
|
||||
0.307697412505553769, 0.310907558127563710, 0.314131931597630143, 0.317370638031222396,
|
||||
0.320623784958230129, 0.323891482377732021, 0.327173842814958593, 0.330470981380537099,
|
||||
0.333783015832108509, 0.337110066638412809, 0.340452257045945450, 0.343809713148291340,
|
||||
0.347182563958251478, 0.350570941482881204, 0.353974980801569250, 0.357394820147290515,
|
||||
0.360830600991175754, 0.364282468130549597, 0.367750569780596226, 0.371235057669821344,
|
||||
0.374736087139491414, 0.378253817247238111, 0.381788410875031348, 0.385340034841733958,
|
||||
0.388908860020464597, 0.392495061461010764, 0.396098818517547080, 0.399720314981931668,
|
||||
0.403359739222868885, 0.407017284331247953, 0.410693148271983222, 0.414387534042706784,
|
||||
0.418100649839684591, 0.421832709231353298, 0.425583931339900579, 0.429354541031341519,
|
||||
0.433144769114574058, 0.436954852549929273, 0.440785034667769915, 0.444635565397727750,
|
||||
0.448506701509214067, 0.452398706863882505, 0.456311852680773566, 0.460246417814923481,
|
||||
0.464202689050278838, 0.468180961407822172, 0.472181538469883255, 0.476204732721683788,
|
||||
0.480250865911249714, 0.484320269428911598, 0.488413284707712059, 0.492530263646148658,
|
||||
0.496671569054796314, 0.500837575128482149, 0.505028667945828791, 0.509245245998136142,
|
||||
0.513487720749743026, 0.517756517232200619, 0.522052074674794864, 0.526374847174186700,
|
||||
0.530725304406193921, 0.535103932383019565, 0.539511234259544614, 0.543947731192649941,
|
||||
0.548413963257921133, 0.552910490428519918, 0.557437893621486324, 0.561996775817277916,
|
||||
0.566587763258951771, 0.571211506738074970, 0.575868682975210544, 0.580559996103683473,
|
||||
0.585286179266300333, 0.590047996335791969, 0.594846243770991268, 0.599681752622167719,
|
||||
0.604555390700549533, 0.609468064928895381, 0.614420723892076803, 0.619414360609039205,
|
||||
0.624450015550274240, 0.629528779928128279, 0.634651799290960050, 0.639820277456438991,
|
||||
0.645035480824251883, 0.650298743114294586, 0.655611470583224665, 0.660975147780241357,
|
||||
0.666391343912380640, 0.671861719900766374, 0.677388036222513090, 0.682972161648791376,
|
||||
0.688616083008527058, 0.694321916130032579, 0.700091918140490099, 0.705928501336797409,
|
||||
0.711834248882358467, 0.717811932634901395, 0.723864533472881599, 0.729995264565802437,
|
||||
0.736207598131266683, 0.742505296344636245, 0.748892447223726720, 0.755373506511754500,
|
||||
0.761953346841546475, 0.768637315803334831, 0.775431304986138326, 0.782341832659861902,
|
||||
0.789376143571198563, 0.796542330428254619, 0.803849483176389490, 0.811307874318219935,
|
||||
0.818929191609414797, 0.826726833952094231, 0.834716292992930375, 0.842915653118441077,
|
||||
0.851346258465123684, 0.860033621203008636, 0.869008688043793165, 0.878309655816146839,
|
||||
0.887984660763399880, 0.898095921906304051, 0.908726440060562912, 0.919991505048360247,
|
||||
0.932060075968990209, 0.945198953453078028, 0.959879091812415930, 0.977101701282731328,
|
||||
1.000000000000000000];
|
||||
pub const ZIG_EXP_R: f64 = 7.697117470131050077;
|
||||
pub static ZIG_EXP_X: [f64; 257] =
|
||||
[8.697117470131052741, 7.697117470131050077, 6.941033629377212577, 6.478378493832569696,
|
||||
6.144164665772472667, 5.882144315795399869, 5.666410167454033697, 5.482890627526062488,
|
||||
5.323090505754398016, 5.181487281301500047, 5.054288489981304089, 4.938777085901250530,
|
||||
4.832939741025112035, 4.735242996601741083, 4.644491885420085175, 4.559737061707351380,
|
||||
4.480211746528421912, 4.405287693473573185, 4.334443680317273007, 4.267242480277365857,
|
||||
4.203313713735184365, 4.142340865664051464, 4.084051310408297830, 4.028208544647936762,
|
||||
3.974606066673788796, 3.923062500135489739, 3.873417670399509127, 3.825529418522336744,
|
||||
3.779270992411667862, 3.734528894039797375, 3.691201090237418825, 3.649195515760853770,
|
||||
3.608428813128909507, 3.568825265648337020, 3.530315889129343354, 3.492837654774059608,
|
||||
3.456332821132760191, 3.420748357251119920, 3.386035442460300970, 3.352149030900109405,
|
||||
3.319047470970748037, 3.286692171599068679, 3.255047308570449882, 3.224079565286264160,
|
||||
3.193757903212240290, 3.164053358025972873, 3.134938858084440394, 3.106389062339824481,
|
||||
3.078380215254090224, 3.050890016615455114, 3.023897504455676621, 2.997382949516130601,
|
||||
2.971327759921089662, 2.945714394895045718, 2.920526286512740821, 2.895747768600141825,
|
||||
2.871364012015536371, 2.847360965635188812, 2.823725302450035279, 2.800444370250737780,
|
||||
2.777506146439756574, 2.754899196562344610, 2.732612636194700073, 2.710636095867928752,
|
||||
2.688959688741803689, 2.667573980773266573, 2.646469963151809157, 2.625639026797788489,
|
||||
2.605072938740835564, 2.584763820214140750, 2.564704126316905253, 2.544886627111869970,
|
||||
2.525304390037828028, 2.505950763528594027, 2.486819361740209455, 2.467904050297364815,
|
||||
2.449198932978249754, 2.430698339264419694, 2.412396812688870629, 2.394289099921457886,
|
||||
2.376370140536140596, 2.358635057409337321, 2.341079147703034380, 2.323697874390196372,
|
||||
2.306486858283579799, 2.289441870532269441, 2.272558825553154804, 2.255833774367219213,
|
||||
2.239262898312909034, 2.222842503111036816, 2.206569013257663858, 2.190438966723220027,
|
||||
2.174449009937774679, 2.158595893043885994, 2.142876465399842001, 2.127287671317368289,
|
||||
2.111826546019042183, 2.096490211801715020, 2.081275874393225145, 2.066180819490575526,
|
||||
2.051202409468584786, 2.036338080248769611, 2.021585338318926173, 2.006941757894518563,
|
||||
1.992404978213576650, 1.977972700957360441, 1.963642687789548313, 1.949412758007184943,
|
||||
1.935280786297051359, 1.921244700591528076, 1.907302480018387536, 1.893452152939308242,
|
||||
1.879691795072211180, 1.866019527692827973, 1.852433515911175554, 1.838931967018879954,
|
||||
1.825513128903519799, 1.812175288526390649, 1.798916770460290859, 1.785735935484126014,
|
||||
1.772631179231305643, 1.759600930889074766, 1.746643651946074405, 1.733757834985571566,
|
||||
1.720942002521935299, 1.708194705878057773, 1.695514524101537912, 1.682900062917553896,
|
||||
1.670349953716452118, 1.657862852574172763, 1.645437439303723659, 1.633072416535991334,
|
||||
1.620766508828257901, 1.608518461798858379, 1.596327041286483395, 1.584191032532688892,
|
||||
1.572109239386229707, 1.560080483527888084, 1.548103603714513499, 1.536177455041032092,
|
||||
1.524300908219226258, 1.512472848872117082, 1.500692176842816750, 1.488957805516746058,
|
||||
1.477268661156133867, 1.465623682245745352, 1.454021818848793446, 1.442462031972012504,
|
||||
1.430943292938879674, 1.419464582769983219, 1.408024891569535697, 1.396623217917042137,
|
||||
1.385258568263121992, 1.373929956328490576, 1.362636402505086775, 1.351376933258335189,
|
||||
1.340150580529504643, 1.328956381137116560, 1.317793376176324749, 1.306660610415174117,
|
||||
1.295557131686601027, 1.284481990275012642, 1.273434238296241139, 1.262412929069615330,
|
||||
1.251417116480852521, 1.240445854334406572, 1.229498195693849105, 1.218573192208790124,
|
||||
1.207669893426761121, 1.196787346088403092, 1.185924593404202199, 1.175080674310911677,
|
||||
1.164254622705678921, 1.153445466655774743, 1.142652227581672841, 1.131873919411078511,
|
||||
1.121109547701330200, 1.110358108727411031, 1.099618588532597308, 1.088889961938546813,
|
||||
1.078171191511372307, 1.067461226479967662, 1.056759001602551429, 1.046063435977044209,
|
||||
1.035373431790528542, 1.024687873002617211, 1.014005623957096480, 1.003325527915696735,
|
||||
0.992646405507275897, 0.981967053085062602, 0.971286240983903260, 0.960602711668666509,
|
||||
0.949915177764075969, 0.939222319955262286, 0.928522784747210395, 0.917815182070044311,
|
||||
0.907098082715690257, 0.896370015589889935, 0.885629464761751528, 0.874874866291025066,
|
||||
0.864104604811004484, 0.853317009842373353, 0.842510351810368485, 0.831682837734273206,
|
||||
0.820832606554411814, 0.809957724057418282, 0.799056177355487174, 0.788125868869492430,
|
||||
0.777164609759129710, 0.766170112735434672, 0.755139984181982249, 0.744071715500508102,
|
||||
0.732962673584365398, 0.721810090308756203, 0.710611050909655040, 0.699362481103231959,
|
||||
0.688061132773747808, 0.676703568029522584, 0.665286141392677943, 0.653804979847664947,
|
||||
0.642255960424536365, 0.630634684933490286, 0.618936451394876075, 0.607156221620300030,
|
||||
0.595288584291502887, 0.583327712748769489, 0.571267316532588332, 0.559100585511540626,
|
||||
0.546820125163310577, 0.534417881237165604, 0.521885051592135052, 0.509211982443654398,
|
||||
0.496388045518671162, 0.483401491653461857, 0.470239275082169006, 0.456886840931420235,
|
||||
0.443327866073552401, 0.429543940225410703, 0.415514169600356364, 0.401214678896277765,
|
||||
0.386617977941119573, 0.371692145329917234, 0.356399760258393816, 0.340696481064849122,
|
||||
0.324529117016909452, 0.307832954674932158, 0.290527955491230394, 0.272513185478464703,
|
||||
0.253658363385912022, 0.233790483059674731, 0.212671510630966620, 0.189958689622431842,
|
||||
0.165127622564187282, 0.137304980940012589, 0.104838507565818778, 0.063852163815001570,
|
||||
0.000000000000000000];
|
||||
pub static ZIG_EXP_F: [f64; 257] =
|
||||
[0.000167066692307963, 0.000454134353841497, 0.000967269282327174, 0.001536299780301573,
|
||||
0.002145967743718907, 0.002788798793574076, 0.003460264777836904, 0.004157295120833797,
|
||||
0.004877655983542396, 0.005619642207205489, 0.006381905937319183, 0.007163353183634991,
|
||||
0.007963077438017043, 0.008780314985808977, 0.009614413642502212, 0.010464810181029981,
|
||||
0.011331013597834600, 0.012212592426255378, 0.013109164931254991, 0.014020391403181943,
|
||||
0.014945968011691148, 0.015885621839973156, 0.016839106826039941, 0.017806200410911355,
|
||||
0.018786700744696024, 0.019780424338009740, 0.020787204072578114, 0.021806887504283581,
|
||||
0.022839335406385240, 0.023884420511558174, 0.024942026419731787, 0.026012046645134221,
|
||||
0.027094383780955803, 0.028188948763978646, 0.029295660224637411, 0.030414443910466622,
|
||||
0.031545232172893622, 0.032687963508959555, 0.033842582150874358, 0.035009037697397431,
|
||||
0.036187284781931443, 0.037377282772959382, 0.038578995503074871, 0.039792391023374139,
|
||||
0.041017441380414840, 0.042254122413316254, 0.043502413568888197, 0.044762297732943289,
|
||||
0.046033761076175184, 0.047316792913181561, 0.048611385573379504, 0.049917534282706379,
|
||||
0.051235237055126281, 0.052564494593071685, 0.053905310196046080, 0.055257689676697030,
|
||||
0.056621641283742870, 0.057997175631200659, 0.059384305633420280, 0.060783046445479660,
|
||||
0.062193415408541036, 0.063615431999807376, 0.065049117786753805, 0.066494496385339816,
|
||||
0.067951593421936643, 0.069420436498728783, 0.070901055162371843, 0.072393480875708752,
|
||||
0.073897746992364746, 0.075413888734058410, 0.076941943170480517, 0.078481949201606435,
|
||||
0.080033947542319905, 0.081597980709237419, 0.083174093009632397, 0.084762330532368146,
|
||||
0.086362741140756927, 0.087975374467270231, 0.089600281910032886, 0.091237516631040197,
|
||||
0.092887133556043569, 0.094549189376055873, 0.096223742550432825, 0.097910853311492213,
|
||||
0.099610583670637132, 0.101322997425953631, 0.103048160171257702, 0.104786139306570145,
|
||||
0.106537004050001632, 0.108300825451033755, 0.110077676405185357, 0.111867631670056283,
|
||||
0.113670767882744286, 0.115487163578633506, 0.117316899211555525, 0.119160057175327641,
|
||||
0.121016721826674792, 0.122886979509545108, 0.124770918580830933, 0.126668629437510671,
|
||||
0.128580204545228199, 0.130505738468330773, 0.132445327901387494, 0.134399071702213602,
|
||||
0.136367070926428829, 0.138349428863580176, 0.140346251074862399, 0.142357645432472146,
|
||||
0.144383722160634720, 0.146424593878344889, 0.148480375643866735, 0.150551185001039839,
|
||||
0.152637142027442801, 0.154738369384468027, 0.156854992369365148, 0.158987138969314129,
|
||||
0.161134939917591952, 0.163298528751901734, 0.165478041874935922, 0.167673618617250081,
|
||||
0.169885401302527550, 0.172113535315319977, 0.174358169171353411, 0.176619454590494829,
|
||||
0.178897546572478278, 0.181192603475496261, 0.183504787097767436, 0.185834262762197083,
|
||||
0.188181199404254262, 0.190545769663195363, 0.192928149976771296, 0.195328520679563189,
|
||||
0.197747066105098818, 0.200183974691911210, 0.202639439093708962, 0.205113656293837654,
|
||||
0.207606827724221982, 0.210119159388988230, 0.212650861992978224, 0.215202151075378628,
|
||||
0.217773247148700472, 0.220364375843359439, 0.222975768058120111, 0.225607660116683956,
|
||||
0.228260293930716618, 0.230933917169627356, 0.233628783437433291, 0.236345152457059560,
|
||||
0.239083290262449094, 0.241843469398877131, 0.244625969131892024, 0.247431075665327543,
|
||||
0.250259082368862240, 0.253110290015629402, 0.255985007030415324, 0.258883549749016173,
|
||||
0.261806242689362922, 0.264753418835062149, 0.267725419932044739, 0.270722596799059967,
|
||||
0.273745309652802915, 0.276793928448517301, 0.279868833236972869, 0.282970414538780746,
|
||||
0.286099073737076826, 0.289255223489677693, 0.292439288161892630, 0.295651704281261252,
|
||||
0.298892921015581847, 0.302163400675693528, 0.305463619244590256, 0.308794066934560185,
|
||||
0.312155248774179606, 0.315547685227128949, 0.318971912844957239, 0.322428484956089223,
|
||||
0.325917972393556354, 0.329440964264136438, 0.332998068761809096, 0.336589914028677717,
|
||||
0.340217149066780189, 0.343880444704502575, 0.347580494621637148, 0.351318016437483449,
|
||||
0.355093752866787626, 0.358908472948750001, 0.362762973354817997, 0.366658079781514379,
|
||||
0.370594648435146223, 0.374573567615902381, 0.378595759409581067, 0.382662181496010056,
|
||||
0.386773829084137932, 0.390931736984797384, 0.395136981833290435, 0.399390684475231350,
|
||||
0.403694012530530555, 0.408048183152032673, 0.412454465997161457, 0.416914186433003209,
|
||||
0.421428728997616908, 0.425999541143034677, 0.430628137288459167, 0.435316103215636907,
|
||||
0.440065100842354173, 0.444876873414548846, 0.449753251162755330, 0.454696157474615836,
|
||||
0.459707615642138023, 0.464789756250426511, 0.469944825283960310, 0.475175193037377708,
|
||||
0.480483363930454543, 0.485871987341885248, 0.491343869594032867, 0.496901987241549881,
|
||||
0.502549501841348056, 0.508289776410643213, 0.514126393814748894, 0.520063177368233931,
|
||||
0.526104213983620062, 0.532253880263043655, 0.538516872002862246, 0.544898237672440056,
|
||||
0.551403416540641733, 0.558038282262587892, 0.564809192912400615, 0.571723048664826150,
|
||||
0.578787358602845359, 0.586010318477268366, 0.593400901691733762, 0.600968966365232560,
|
||||
0.608725382079622346, 0.616682180915207878, 0.624852738703666200, 0.633251994214366398,
|
||||
0.641896716427266423, 0.650805833414571433, 0.660000841079000145, 0.669506316731925177,
|
||||
0.679350572264765806, 0.689566496117078431, 0.700192655082788606, 0.711274760805076456,
|
||||
0.722867659593572465, 0.735038092431424039, 0.747868621985195658, 0.761463388849896838,
|
||||
0.775956852040116218, 0.791527636972496285, 0.808421651523009044, 0.826993296643051101,
|
||||
0.847785500623990496, 0.871704332381204705, 0.900469929925747703, 0.938143680862176477,
|
||||
1.000000000000000000];
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Pseudo random number generators are algorithms to produce *apparently
|
||||
//! random* numbers deterministically, and usually fairly quickly.
|
||||
//!
|
||||
//! So long as the algorithm is computationally secure, is initialised with
|
||||
//! sufficient entropy (i.e. unknown by an attacker), and its internal state is
|
||||
//! also protected (unknown to an attacker), the output will also be
|
||||
//! *computationally secure*. Computationally Secure Pseudo Random Number
|
||||
//! Generators (CSPRNGs) are thus suitable sources of random numbers for
|
||||
//! cryptography. There are a couple of gotchas here, however. First, the seed
|
||||
//! used for initialisation must be unknown. Usually this should be provided by
|
||||
//! the operating system and should usually be secure, however this may not
|
||||
//! always be the case (especially soon after startup). Second, user-space
|
||||
//! memory may be vulnerable, for example when written to swap space, and after
|
||||
//! forking a child process should reinitialise any user-space PRNGs. For this
|
||||
//! reason it may be preferable to source random numbers directly from the OS
|
||||
//! for cryptographic applications.
|
||||
//!
|
||||
//! PRNGs are also widely used for non-cryptographic uses: randomised
|
||||
//! algorithms, simulations, games. In these applications it is usually not
|
||||
//! important for numbers to be cryptographically *unguessable*, but even
|
||||
//! distribution and independence from other samples (from the point of view
|
||||
//! of someone unaware of the algorithm used, at least) may still be important.
|
||||
//! Good PRNGs should satisfy these properties, but do not take them for
|
||||
//! granted; Wikipedia's article on
|
||||
//! [Pseudorandom number generators](https://en.wikipedia.org/wiki/Pseudorandom_number_generator)
|
||||
//! provides some background on this topic.
|
||||
//!
|
||||
//! Care should be taken when seeding (initialising) PRNGs. Some PRNGs have
|
||||
//! short periods for some seeds. If one PRNG is seeded from another using the
|
||||
//! same algorithm, it is possible that both will yield the same sequence of
|
||||
//! values (with some lag).
|
||||
|
||||
mod chacha;
|
||||
mod isaac;
|
||||
mod isaac64;
|
||||
mod xorshift;
|
||||
|
||||
pub use self::chacha::ChaChaRng;
|
||||
pub use self::isaac::IsaacRng;
|
||||
pub use self::isaac64::Isaac64Rng;
|
||||
pub use self::xorshift::XorShiftRng;
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче