diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..428ed22 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-wasip1] +runner = ["./scripts/wasmtime-wrapper.sh"] diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a92d664..7240575 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,3 +28,22 @@ jobs: run: cp Cargo.lock.msrv Cargo.lock - name: Test run: cargo check --all-features + + test-wasi: + name: Test on WASI + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: wasm32-wasip1 + - uses: Swatinem/rust-cache@v2 + - name: Install WasmTime + run: | + curl -LO https://github.com/bytecodealliance/wasmtime/releases/download/v13.0.0/wasmtime-v13.0.0-x86_64-linux.tar.xz + tar xvf wasmtime-v13.0.0-x86_64-linux.tar.xz + echo `pwd`/wasmtime-v13.0.0-x86_64-linux >> $GITHUB_PATH + - name: Test + run: make wasi-test diff --git a/Makefile b/Makefile index af6e831..bb51dbf 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,10 @@ test: @cargo test --no-default-features @cargo test --no-default-features --features bytes +.PHONY: wasi-test +wasi-test: + @cargo test --all-features --target=wasm32-wasip1 -- --nocapture + format: @rustup component add rustfmt 2> /dev/null @cargo fmt --all diff --git a/scripts/wasmtime-wrapper.sh b/scripts/wasmtime-wrapper.sh new file mode 100755 index 0000000..d6ece59 --- /dev/null +++ b/scripts/wasmtime-wrapper.sh @@ -0,0 +1,4 @@ +#!/bin/bash +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $SCRIPT_DIR/.. +wasmtime run --max-wasm-stack=4194304 --env INSTA_WORKSPACE_ROOT=/ --mapdir "/::$(pwd)" -- "$@" diff --git a/src/algorithms/lcs.rs b/src/algorithms/lcs.rs index 5f081db..1c6a43b 100644 --- a/src/algorithms/lcs.rs +++ b/src/algorithms/lcs.rs @@ -7,7 +7,7 @@ use std::ops::{Index, Range}; use crate::algorithms::utils::{common_prefix_len, common_suffix_len, is_empty_range}; use crate::algorithms::DiffHook; -use crate::Instant; +use crate::deadline_support::{deadline_exceeded, Instant}; /// LCS diff algorithm. /// @@ -166,10 +166,8 @@ where for i in (0..new_len).rev() { // are we running for too long? give up on the table - if let Some(deadline) = deadline { - if Instant::now() > deadline { - return None; - } + if deadline_exceeded(deadline) { + return None; } for j in (0..old_len).rev() { diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index 0953cd7..008ade5 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -42,7 +42,7 @@ pub(crate) mod utils; use std::hash::Hash; use std::ops::{Index, Range}; -use crate::Instant; +use crate::deadline_support::Instant; pub use capture::Capture; pub use compact::Compact; pub use hook::{DiffHook, NoFinishHook}; diff --git a/src/algorithms/myers.rs b/src/algorithms/myers.rs index 20ddf6c..c6c1bf7 100644 --- a/src/algorithms/myers.rs +++ b/src/algorithms/myers.rs @@ -23,7 +23,7 @@ use std::ops::{Index, IndexMut, Range}; use crate::algorithms::utils::{common_prefix_len, common_suffix_len, is_empty_range}; use crate::algorithms::DiffHook; -use crate::Instant; +use crate::deadline_support::{deadline_exceeded, Instant}; /// Myers' diff algorithm. /// @@ -175,10 +175,8 @@ where for d in 0..d_max as isize { // are we running for too long? - if let Some(deadline) = deadline { - if Instant::now() > deadline { - break; - } + if deadline_exceeded(deadline) { + break; } // Forward path diff --git a/src/algorithms/patience.rs b/src/algorithms/patience.rs index 1cce122..c207c03 100644 --- a/src/algorithms/patience.rs +++ b/src/algorithms/patience.rs @@ -12,7 +12,7 @@ use std::hash::Hash; use std::ops::{Index, Range}; use crate::algorithms::{myers, DiffHook, NoFinishHook, Replace}; -use crate::Instant; +use crate::deadline_support::Instant; use super::utils::{unique, UniqueItem}; diff --git a/src/common.rs b/src/common.rs index d7c8e14..8ce9a92 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use std::ops::{Index, Range}; use crate::algorithms::{diff_deadline, Capture, Compact, Replace}; -use crate::Instant; +use crate::deadline_support::Instant; use crate::{Algorithm, DiffOp}; /// Creates a diff between old and new with the given algorithm capturing the ops. diff --git a/src/deadline_support.rs b/src/deadline_support.rs new file mode 100644 index 0000000..1066d41 --- /dev/null +++ b/src/deadline_support.rs @@ -0,0 +1,37 @@ +use std::time::Duration; + +#[cfg(not(feature = "wasm32_web_time"))] +pub use std::time::Instant; + +/// WASM (browser) specific instant type. +/// +/// This type is only available when the `wasm32_web_time` feature is enabled. In that +/// case this is an alias for [`web_time::Instant`]. +#[cfg(feature = "wasm32_web_time")] +pub use web_time::Instant; + +/// Checks if a deadline was exeeded. +pub fn deadline_exceeded(deadline: Option) -> bool { + #[allow(unreachable_code)] + match deadline { + Some(deadline) => { + #[cfg(all(target_arch = "wasm32", not(feature = "wasm32_web_time")))] + { + return false; + } + Instant::now() > deadline + } + None => false, + } +} + +/// Converst a duration into a deadline. This can be a noop on wasm +#[allow(unused)] +pub fn duration_to_deadline(add: Duration) -> Option { + #[allow(unreachable_code)] + #[cfg(all(target_arch = "wasm32", not(feature = "wasm32_web_time")))] + { + return None; + } + Instant::now().checked_add(add) +} diff --git a/src/lib.rs b/src/lib.rs index b558863..3b09bc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,6 +121,11 @@ //! The [`TextDiff`] type also lets you configure a deadline and/or timeout //! when performing a text diff. //! +//! Note that on wasm targets calling [`Instant::now`] will result in a panic +//! unless you enable the `wasm32_web_time` feataure. By default similar will +//! silently disable the deadline checks internally unless that feature is +//! enabled. +//! //! # Feature Flags //! //! The crate by default does not have any dependencies however for some use @@ -156,6 +161,7 @@ pub mod udiff; pub mod utils; mod common; +mod deadline_support; #[cfg(feature = "text")] mod text; mod types; @@ -165,13 +171,6 @@ pub use self::common::*; pub use self::text::*; pub use self::types::*; -/// Internal alias for portability -#[cfg(not(feature = "wasm32_web_time"))] -pub(crate) use std::time::Instant; - -/// WASM (browser) specific instant type. -/// -/// This type is only available when the `wasm32_web_time` feature is enabled. In that -/// case this is an alias for [`web_time::Instant`]. +// re-export the type for web-time feature #[cfg(feature = "wasm32_web_time")] -pub use web_time::Instant; +pub use deadline_support::Instant; diff --git a/src/text/inline.rs b/src/text/inline.rs index 3052784..bd2fd5d 100644 --- a/src/text/inline.rs +++ b/src/text/inline.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; use std::fmt; +use crate::deadline_support::Instant; use crate::text::{DiffableStr, TextDiff}; use crate::types::{Algorithm, Change, ChangeTag, DiffOp, DiffTag}; -use crate::Instant; use crate::{capture_diff_deadline, get_diff_ratio}; use std::ops::Index; diff --git a/src/text/mod.rs b/src/text/mod.rs index ca6ca97..5acf114 100644 --- a/src/text/mod.rs +++ b/src/text/mod.rs @@ -15,9 +15,9 @@ pub use self::inline::InlineChange; use self::utils::{upper_seq_ratio, QuickSeqRatio}; use crate::algorithms::IdentifyDistinct; +use crate::deadline_support::{duration_to_deadline, Instant}; use crate::iter::{AllChangesIter, ChangesIter}; use crate::udiff::UnifiedDiff; -use crate::Instant; use crate::{capture_diff_deadline, get_diff_ratio, group_diff_ops, Algorithm, DiffOp}; #[derive(Debug, Clone, Copy)] @@ -27,10 +27,10 @@ enum Deadline { } impl Deadline { - fn into_instant(self) -> Instant { + fn into_instant(self) -> Option { match self { - Deadline::Absolute(instant) => instant, - Deadline::Relative(duration) => Instant::now() + duration, + Deadline::Absolute(instant) => Some(instant), + Deadline::Relative(duration) => duration_to_deadline(duration), } } } @@ -319,7 +319,7 @@ impl TextDiffConfig { new: Cow<'bufs, [&'new T]>, newline_terminated: bool, ) -> TextDiff<'old, 'new, 'bufs, T> { - let deadline = self.deadline.map(|x| x.into_instant()); + let deadline = self.deadline.and_then(|x| x.into_instant()); let ops = if old.len() > 100 || new.len() > 100 { let ih = IdentifyDistinct::::new(&old[..], 0..old.len(), &new[..], 0..new.len()); capture_diff_deadline( @@ -548,7 +548,9 @@ impl<'old, 'new, 'bufs, T: DiffableStr + ?Sized + 'old + 'new> TextDiff<'old, 'n where 'slf: 'old + 'new, { - inline::iter_inline_changes(self, op, Some(Instant::now() + Duration::from_millis(500))) + use crate::deadline_support::duration_to_deadline; + + inline::iter_inline_changes(self, op, duration_to_deadline(Duration::from_millis(500))) } /// Iterates over the changes the op expands to with inline emphasis with a deadline.