diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2020-10-21 20:26:22 +0200 |
---|---|---|
committer | Jeff Vander Stoep <jeffv@google.com> | 2020-10-21 21:12:25 +0200 |
commit | 6b6c8e627bdeddaf9ee752618ed96119c668ebfc (patch) | |
tree | f6274f90892c0f9baa931546dcbbe0e24b54c863 | |
parent | 86e10ee1b176558f0bed700530de57a0d81645ee (diff) | |
download | instant-6b6c8e627bdeddaf9ee752618ed96119c668ebfc.tar.gz |
Initial import of Instant crate
Version 0.1.7
Bug: 169931010
Test: build
Change-Id: I12dd69b9b9ee5217d3ac5499cdc353b297d13e92
-rw-r--r-- | .cargo_vcs_info.json | 5 | ||||
-rw-r--r-- | .circleci/config.yml | 33 | ||||
-rw-r--r-- | .gitignore | 12 | ||||
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | Android.bp | 15 | ||||
-rw-r--r-- | Cargo.toml | 84 | ||||
-rw-r--r-- | LICENSE | 27 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | MODULE_LICENSE_BSD_LIKE | 0 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | README.md | 124 | ||||
-rw-r--r-- | src/lib.rs | 22 | ||||
-rw-r--r-- | src/native.rs | 7 | ||||
-rw-r--r-- | src/wasm.rs | 129 | ||||
-rw-r--r-- | tests/wasm.rs | 46 |
15 files changed, 526 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..bb7745e --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "96feb8a208a8a7cd3c2d09afba3ba236821ba57b" + } +} diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..878e288 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,33 @@ +version: 2.1 + +executors: + rust-executor: + docker: + - image: rust:latest + +jobs: + build: + executor: rust-executor + steps: + - checkout + - run: + name: install cargo-web + command: cargo install -f cargo-web; + - run: + name: build + command: cargo build --verbose; + - run: + name: build --features stdweb + command: cargo web build --verbose --target wasm32-unknown-unknown --features "stdweb"; + - run: + name: build --features wasm-bindgen + command: cargo build --verbose --target wasm32-unknown-unknown --features "wasm-bindgen"; + - run: + name: build --features now + command: cargo build --verbose --features now; + - run: + name: build --features now stdweb + command: cargo web build --verbose --target wasm32-unknown-unknown --features "now stdweb"; + - run: + name: build --features now wasm-bindgen + command: cargo build --verbose --target wasm32-unknown-unknown --features "now wasm-bindgen";
\ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3722785 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.swp +*.swo +*.so +*.rlib +*.dSYM +*.dylib +cargo.lock +Cargo.lock +target +.idea +wasm-pack.log +bin @@ -0,0 +1,2 @@ +Main developer: + * Sébastien Crozet <developer@crozet.re>
\ No newline at end of file diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..c75dd0b --- /dev/null +++ b/Android.bp @@ -0,0 +1,15 @@ +// This file is generated by cargo2android.py --run --device --dependencies. + +rust_library { + name: "libinstant", + host_supported: true, + crate_name: "instant", + srcs: ["src/lib.rs"], + edition: "2018", + rustlibs: [ + "libcfg_if", + ], +} + +// dependent_library ["feature_list"] +// cfg-if-0.1.10 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1031381 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,84 @@ +# 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 = "instant" +version = "0.1.7" +authors = ["sebcrozet <developer@crozet.re>"] +description = "A partial replacement for std::time::Instant that works on WASM too." +readme = "README.md" +keywords = ["time", "wasm"] +license = "BSD-3-Clause" +repository = "https://github.com/sebcrozet/instant" +[dependencies.cfg-if] +version = "0.1" +[dev-dependencies.wasm-bindgen-test] +version = "0.2" + +[features] +now = ["time"] +wasm-bindgen = ["js-sys", "wasm-bindgen_rs", "web-sys"] +[target.asmjs-unknown-emscripten.dependencies.js-sys] +version = "0.3" +optional = true + +[target.asmjs-unknown-emscripten.dependencies.stdweb] +version = "0.4" +optional = true + +[target.asmjs-unknown-emscripten.dependencies.wasm-bindgen_rs] +version = "0.2" +optional = true +package = "wasm-bindgen" + +[target.asmjs-unknown-emscripten.dependencies.web-sys] +version = "0.3" +features = ["Window", "Performance", "PerformanceTiming"] +optional = true +[target."cfg(not(any(feature = \"stdweb\", feature = \"wasm-bindgen\")))".dependencies.time] +version = "0.1" +optional = true +[target.wasm32-unknown-emscripten.dependencies.js-sys] +version = "0.3" +optional = true + +[target.wasm32-unknown-emscripten.dependencies.stdweb] +version = "0.4" +optional = true + +[target.wasm32-unknown-emscripten.dependencies.wasm-bindgen_rs] +version = "0.2" +optional = true +package = "wasm-bindgen" + +[target.wasm32-unknown-emscripten.dependencies.web-sys] +version = "0.3" +features = ["Window", "Performance", "PerformanceTiming"] +optional = true +[target.wasm32-unknown-unknown.dependencies.js-sys] +version = "0.3" +optional = true + +[target.wasm32-unknown-unknown.dependencies.stdweb] +version = "0.4" +optional = true + +[target.wasm32-unknown-unknown.dependencies.wasm-bindgen_rs] +version = "0.2" +optional = true +package = "wasm-bindgen" + +[target.wasm32-unknown-unknown.dependencies.web-sys] +version = "0.3" +features = ["Window", "Performance", "PerformanceTiming"] +optional = true @@ -0,0 +1,27 @@ +Copyright (c) 2019, Sébastien Crozet +All rights reserved. + +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. + +3. Neither the name of the author 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 LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..0b7ab54 --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "instant" +description: "A partial replacement for std::time::Instant that works on WASM too." +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/instant" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/instant/instant-0.1.7.crate" + } + version: "0.1.7" + license_type: NOTICE + last_upgrade_date { + year: 2020 + month: 10 + day: 20 + } +} diff --git a/MODULE_LICENSE_BSD_LIKE b/MODULE_LICENSE_BSD_LIKE new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_BSD_LIKE @@ -0,0 +1 @@ +include platform/prebuilts/rust:/OWNERS diff --git a/README.md b/README.md new file mode 100644 index 0000000..28f6bdd --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Instant + +If you call `std::time::Instant::now()` on a WASM platform, it will panic. This crate provides a partial +replacement for `std::time::Instant` that works on WASM too. This defines the type `instant::Instant` which is: + +* A struct emulating the behavior of **std::time::Instant** if you are targeting `wasm32-unknown-unknown` or `wasm32-unknown-asmjs` +**and** you enabled either the `stdweb` or the `wasm-bindgen` feature. This emulation is based on the javascript `performance.now()` function. +* A type alias for `std::time::Instant` otherwise. + + + +Note that even if the **stdweb** or **wasm-bindgen** feature is enabled, this crate will continue to rely on `std::time::Instant` +as long as you are not targeting wasm32. This allows for portable code that will work on both native and WASM platforms. + +### The feature `now`. +By enabling the feature `now` the function `instant::now()` will be exported and will either: + +* Call `performance.now()` when compiling for a WASM platform with the features **stdweb** or **wasm-bindgen** enabled, or using a custom javascript function. +* Call `time::precise_time_s() * 1000.0` otherwise. + +The result is expressed in milliseconds. + +## Examples +### Using `instant` for a native platform. +_Cargo.toml_: +```toml +[dependencies] +instant = "0.1" +``` + +_main.rs_: +```rust +fn main() { + // Will be the same as `std::time::Instant`. + let now = instant::Instant::new(); +} +``` + +----- + +### Using `instant` for a WASM platform. +This example shows the use of the `stdweb` feature. It would be similar with `wasm-bindgen`. + +_Cargo.toml_: +```toml +[dependencies] +instant = { version = "0.1", features = [ "stdweb" ] } +``` + +_main.rs_: +```rust +fn main() { + // Will emulate `std::time::Instant` based on `performance.now()`. + let now = instant::Instant::new(); +} +``` + +----- + +### Using `instant` for any platform enabling a feature transitively. +_Cargo.toml_: +```toml +[features] +stdweb = [ "instant/stdweb" ] +wasm-bindgen = [ "instant/wasm-bindgen" ] + +[dependencies] +instant = "0.1" +``` + +_lib.rs_: +```rust +fn my_function() { + // Will select the proper implementation depending on the + // feature selected by the user. + let now = instant::Instant::new(); +} +``` + +----- + +### Using the feature `now`. +_Cargo.toml_: +```toml +[features] +stdweb = [ "instant/stdweb" ] +wasm-bindgen = [ "instant/wasm-bindgen" ] + +[dependencies] +instant = { version = "0.1", features = [ "now" ] } +``` + +_lib.rs_: +```rust +fn my_function() { + // Will select the proper implementation depending on the + // feature selected by the user. + let now_instant = instant::Instant::new(); + let now_milliseconds = instant::now(); // In milliseconds. +} +``` + +### Using the feature `now` without `stdweb` or `wasm-bindgen`. +_Cargo.toml_: +```toml +[dependencies] +instant = { version = "0.", features = [ "now" ] } +``` + +_lib.rs_: +```rust +fn my_function() { + // Will use the 'now' javascript implementation. + let now_instant = instant::Instant::new(); + let now_milliseconds = instant::now(); // In milliseconds. +} +``` + +_javascript WASM bindings file_: +```js +function now() { + return Date.now() / 1000.0; +} +``` diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..077c9d4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,22 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_arch = "wasm32", not(target_os = "wasi")), + target_arch = "asmjs" + ))] { + #[cfg(all(feature = "stdweb", not(feature = "wasm-bindgen")))] + #[macro_use] + extern crate stdweb; + + mod wasm; + pub use wasm::Instant; + #[cfg(feature = "now")] + pub use crate::wasm::now; + } else { + mod native; + pub use native::Instant; + #[cfg(feature = "now")] + pub use native::now; + } +} + +pub use std::time::Duration; diff --git a/src/native.rs b/src/native.rs new file mode 100644 index 0000000..37d5da4 --- /dev/null +++ b/src/native.rs @@ -0,0 +1,7 @@ +pub type Instant = std::time::Instant; + +/// The current time, in milliseconds. +#[cfg(feature = "now")] +pub fn now() -> f64 { + time::precise_time_s() * 1000.0 +} diff --git a/src/wasm.rs b/src/wasm.rs new file mode 100644 index 0000000..829ef3b --- /dev/null +++ b/src/wasm.rs @@ -0,0 +1,129 @@ +use std::ops::{Add, AddAssign, Sub, SubAssign}; +use std::time::Duration; + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)] +pub struct Instant(Duration); + +impl Ord for Instant { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.partial_cmp(other) + .expect("an instant should never be NaN or Inf.") + } +} +impl Eq for Instant {} + +impl Instant { + #[inline] + pub fn now() -> Self { + Instant(duration_from_f64(now())) + } + + #[inline] + pub fn duration_since(&self, earlier: Instant) -> Duration { + assert!( + earlier.0 <= self.0, + "`earlier` cannot be later than `self`." + ); + self.0 - earlier.0 + } + + #[inline] + pub fn elapsed(&self) -> Duration { + Self::now().duration_since(*self) + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[inline] + pub fn checked_add(&self, duration: Duration) -> Option<Instant> { + self.0.checked_add(duration).map(Instant) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[inline] + pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { + self.0.checked_sub(duration).map(Instant) + } +} + +impl Add<Duration> for Instant { + type Output = Self; + + #[inline] + fn add(self, rhs: Duration) -> Self { + Instant(self.0 + rhs) + } +} + +impl AddAssign<Duration> for Instant { + #[inline] + fn add_assign(&mut self, rhs: Duration) { + self.0 += rhs + } +} + +impl Sub<Duration> for Instant { + type Output = Self; + + #[inline] + fn sub(self, rhs: Duration) -> Self { + Instant(self.0 - rhs) + } +} + +impl Sub<Instant> for Instant { + type Output = Duration; + + #[inline] + fn sub(self, rhs: Instant) -> Duration { + self.duration_since(rhs) + } +} + +impl SubAssign<Duration> for Instant { + #[inline] + fn sub_assign(&mut self, rhs: Duration) { + self.0 -= rhs + } +} + +fn duration_from_f64(millis: f64) -> Duration { + Duration::from_millis(millis.trunc() as u64) + + Duration::from_nanos((millis.fract() * 1.0e6) as u64) +} + +#[cfg(all(feature = "stdweb", not(feature = "wasm-bindgen")))] +#[allow(unused_results)] // Needed because the js macro triggers it. +pub fn now() -> f64 { + use stdweb::unstable::TryInto; + + // https://developer.mozilla.org/en-US/docs/Web/API/Performance/now + let v = js! { return performance.now(); }; + v.try_into().unwrap() +} + +#[cfg(feature = "wasm-bindgen")] +pub fn now() -> f64 { + use wasm_bindgen_rs::prelude::*; + use wasm_bindgen_rs::JsCast; + js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("performance")) + .expect("failed to get performance from global object") + .unchecked_into::<web_sys::Performance>() + .now() +} + +// The JS now function is in a module so it won't have to be renamed +#[cfg(not(any(feature = "wasm-bindgen", feature = "stdweb")))] +mod js { + extern "C" { + pub fn now() -> f64; + } +} +// Make the unsafe extern function "safe" so it can be called like the other 'now' functions +#[cfg(not(any(feature = "wasm-bindgen", feature = "stdweb")))] +pub fn now() -> f64 { + unsafe { js::now() } +} diff --git a/tests/wasm.rs b/tests/wasm.rs new file mode 100644 index 0000000..b1577ad --- /dev/null +++ b/tests/wasm.rs @@ -0,0 +1,46 @@ +extern crate wasm_bindgen_test; + +use instant::Instant; +use std::time::Duration; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); +// run these tests using: wasm-pack test --chrome --headless -- --features wasm-bindgen + +#[wasm_bindgen_test] +fn test_instant_now() { + let now = Instant::now(); + assert!(now.elapsed().as_nanos() > 0); +} + +#[wasm_bindgen_test] +fn test_duration() { + let now = Instant::now(); + let one_sec = Duration::from_secs(1); + assert!(now.elapsed() < one_sec); +} + +// Duration::new will overflow when you have u64::MAX seconds and one billion nanoseconds. +// <https://doc.rust-lang.org/std/time/struct.Duration.html#method.new> +const ONE_BILLION: u32 = 1_000_000_000; + +#[wasm_bindgen_test] +fn test_checked_add() { + let now = Instant::now(); + + assert!(now.checked_add(Duration::from_millis(1)).is_some()); + assert_eq!( + None, + now.checked_add(Duration::new(u64::MAX, ONE_BILLION - 1)) + ); +} + +#[wasm_bindgen_test] +fn test_checked_sub() { + let now = Instant::now(); + + assert!(now.checked_sub(Duration::from_millis(1)).is_some()); + assert!(now + .checked_sub(Duration::new(u64::MAX, ONE_BILLION - 1)) + .is_none()); +} |