diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6062667d0..38b98ffdd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -59,10 +59,10 @@ jobs: steps: - uses: actions/checkout@v3 - run: rustup update - - uses: stellar/binaries@v24 + - uses: stellar/binaries@v31 with: name: cargo-semver-checks - version: 0.32.0 + version: 0.35.0 - run: cargo semver-checks --exclude soroban-simulation build-and-test: diff --git a/Cargo.lock b/Cargo.lock index 474c44c57..74e6e4609 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1352,7 +1352,7 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "soroban-bench-utils" -version = "21.2.1" +version = "21.2.2" dependencies = [ "perf-event", "soroban-env-common", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "soroban-builtin-sdk-macros" -version = "21.2.1" +version = "21.2.2" dependencies = [ "itertools", "proc-macro2", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "soroban-env-common" -version = "21.2.1" +version = "21.2.2" dependencies = [ "arbitrary", "crate-git-revision", @@ -1390,7 +1390,7 @@ dependencies = [ [[package]] name = "soroban-env-guest" -version = "21.2.1" +version = "21.2.2" dependencies = [ "soroban-env-common", "static_assertions", @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "soroban-env-host" -version = "21.2.1" +version = "21.2.2" dependencies = [ "arbitrary", "backtrace", @@ -1452,7 +1452,7 @@ dependencies = [ [[package]] name = "soroban-env-macros" -version = "21.2.1" +version = "21.2.2" dependencies = [ "itertools", "proc-macro2", @@ -1465,7 +1465,7 @@ dependencies = [ [[package]] name = "soroban-simulation" -version = "21.2.1" +version = "21.2.2" dependencies = [ "anyhow", "pretty_assertions", @@ -1479,7 +1479,7 @@ dependencies = [ [[package]] name = "soroban-synth-wasm" -version = "21.2.1" +version = "21.2.2" dependencies = [ "arbitrary", "expect-test", @@ -1493,7 +1493,7 @@ dependencies = [ [[package]] name = "soroban-test-wasms" -version = "21.2.1" +version = "21.2.2" [[package]] name = "soroban-wasmi" @@ -1596,7 +1596,7 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "test_no_std" -version = "21.2.1" +version = "21.2.2" dependencies = [ "soroban-env-common", ] diff --git a/Cargo.toml b/Cargo.toml index 70bafeba2..b075e55ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,15 +20,15 @@ exclude = ["soroban-test-wasms/wasm-workspace"] # NB: When bumping the major version make sure to clean up the # code guarded by `unstable-*` features to make it enabled # unconditionally. -version = "21.2.1" +version = "21.2.2" rust-version = "1.74.0" [workspace.dependencies] -soroban-env-common = { version = "=21.2.1", path = "soroban-env-common", default-features = false } -soroban-env-guest = { version = "=21.2.1", path = "soroban-env-guest" } -soroban-env-host = { version = "=21.2.1", path = "soroban-env-host" } -soroban-env-macros = { version = "=21.2.1", path = "soroban-env-macros" } -soroban-builtin-sdk-macros = { version = "=21.2.1", path = "soroban-builtin-sdk-macros" } +soroban-env-common = { version = "=21.2.2", path = "soroban-env-common", default-features = false } +soroban-env-guest = { version = "=21.2.2", path = "soroban-env-guest" } +soroban-env-host = { version = "=21.2.2", path = "soroban-env-host" } +soroban-env-macros = { version = "=21.2.2", path = "soroban-env-macros" } +soroban-builtin-sdk-macros = { version = "=21.2.2", path = "soroban-builtin-sdk-macros" } # NB: this must match the wasmparser version wasmi is using wasmparser = "=0.116.1" diff --git a/soroban-env-host/src/host/metered_map.rs b/soroban-env-host/src/host/metered_map.rs index d3bb13162..22bbcba6d 100644 --- a/soroban-env-host/src/host/metered_map.rs +++ b/soroban-env-host/src/host/metered_map.rs @@ -7,6 +7,8 @@ use crate::{ use std::{borrow::Borrow, cmp::Ordering, marker::PhantomData}; +use super::metered_vector::binary_search_by_pre_rust_182; + const MAP_OOB: Error = Error::from_type_and_code(ScErrorType::Object, ScErrorCode::IndexBounds); pub struct MeteredOrdMap { @@ -161,7 +163,7 @@ where let _span = tracy_span!("map lookup"); self.charge_binsearch(ctx)?; let mut err: Option = None; - let res = self.map.binary_search_by(|probe| { + let res = binary_search_by_pre_rust_182(self.map.as_slice(), |probe| { // We've already hit an error, return Ordering::Equal // to terminate search asap. if err.is_some() { diff --git a/soroban-env-host/src/host/metered_vector.rs b/soroban-env-host/src/host/metered_vector.rs index e492dce7d..29b157182 100644 --- a/soroban-env-host/src/host/metered_vector.rs +++ b/soroban-env-host/src/host/metered_vector.rs @@ -287,7 +287,7 @@ where { self.charge_binsearch(budget)?; let mut err: Option = None; - let res = self.vec.binary_search_by(|probe| { + let res = binary_search_by_pre_rust_182(self.vec.as_slice(), |probe| { // We've already hit an error, return Ordering::Equal // to terminate search asap. if err.is_some() { @@ -373,3 +373,57 @@ where >>::compare(self, &a.vec, &b.vec) } } + +/// This is a copy of implementation of Rust stdlib `binary_search_by` function +/// pre-Rust-1.82 with a minor modifications. +/// +/// We cannot rely on the standard library implementation as it might change and +/// thus change the observed metering as well. Thus we are just using the same +/// hardcoded implementation. +/// +/// The code is copied from the following file revision: +/// https://github.com/rust-lang/rust/blob/8f7af88b33771ab5ec92a2a767a97a068f2ea17b/library/core/src/slice/mod.rs#L2769 +/// Modifications are no-op: switched &self to an explicit `slice` argument, +/// removed assertions and unsafe blocks. +pub(crate) fn binary_search_by_pre_rust_182(slice: &[T], mut f: F) -> Result +where + F: FnMut(&T) -> std::cmp::Ordering, +{ + // INVARIANTS: + // - 0 <= left <= left + size = right <= self.len() + // - f returns Less for everything in self[..left] + // - f returns Greater for everything in self[right..] + let mut size = slice.len(); + let mut left = 0; + let mut right = size; + while left < right { + let mid = left + size / 2; + + // SAFETY: the while condition means `size` is strictly positive, so + // `size/2 < size`. Thus `left + size/2 < left + size`, which + // coupled with the `left + size <= self.len()` invariant means + // we have `left + size/2 < self.len()`, and this is in-bounds. + let cmp = f(slice.get(mid).unwrap()); + + // This control flow produces conditional moves, which results in + // fewer branches and instructions than if/else or matching on + // cmp::Ordering. + // This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx. + left = if cmp == std::cmp::Ordering::Less { + mid + 1 + } else { + left + }; + right = if cmp == std::cmp::Ordering::Greater { + mid + } else { + right + }; + if cmp == std::cmp::Ordering::Equal { + return Ok(mid); + } + + size = right - left; + } + Err(left) +}