diff --git a/Cargo.lock b/Cargo.lock index 8db89b9ef01303..ea01a6d219091b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3722,15 +3722,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "percentage" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" -dependencies = [ - "num", -] - [[package]] name = "pest" version = "2.1.3" @@ -5226,7 +5217,6 @@ dependencies = [ "num_cpus", "num_enum", "ouroboros", - "percentage", "qualifier_attr", "rand 0.8.5", "rand_chacha 0.3.1", @@ -6638,7 +6628,6 @@ dependencies = [ "log", "num-derive", "num-traits", - "percentage", "rand 0.8.5", "rustc_version 0.4.0", "serde", @@ -6649,6 +6638,7 @@ dependencies = [ "solana-metrics", "solana-sdk", "solana_rbpf", + "solana_utils", "thiserror", ] @@ -6949,7 +6939,6 @@ dependencies = [ "num_cpus", "num_enum", "ouroboros", - "percentage", "qualifier_attr", "rand 0.8.5", "rand_chacha 0.3.1", @@ -6985,6 +6974,7 @@ dependencies = [ "solana-vote-program", "solana-zk-token-proof-program", "solana-zk-token-sdk", + "solana_utils", "static_assertions", "strum", "strum_macros", @@ -7210,7 +7200,6 @@ dependencies = [ "log", "nix 0.26.4", "pem", - "percentage", "quinn", "quinn-proto", "rand 0.8.5", @@ -7219,6 +7208,7 @@ dependencies = [ "solana-metrics", "solana-perf", "solana-sdk", + "solana_utils", "thiserror", "tokio", "x509-parser", @@ -7230,7 +7220,6 @@ version = "1.18.0" dependencies = [ "itertools", "log", - "percentage", "rustc_version 0.4.0", "solana-accounts-db", "solana-bpf-loader-program", @@ -7243,6 +7232,7 @@ dependencies = [ "solana-program-runtime", "solana-sdk", "solana-system-program", + "solana_utils", ] [[package]] @@ -7745,6 +7735,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "solana_utils" +version = "1.18.0" +dependencies = [ + "clap 2.33.3", + "num", + "solana-runtime", + "solana-version", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 366ef313e55e63..cd77002bee88c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -262,7 +262,7 @@ ouroboros = "0.15.6" parking_lot = "0.12" pbkdf2 = { version = "0.11.0", default-features = false } pem = "1.1.1" -percentage = "0.1.0" +# percentage = "0.1.0" pickledb = { version = "0.5.1", default-features = false } predicates = "2.1" pretty-hex = "0.3.0" @@ -390,6 +390,7 @@ solana-zk-keygen = { path = "zk-keygen", version = "=1.18.0" } solana-zk-token-proof-program = { path = "programs/zk-token-proof", version = "=1.18.0" } solana-zk-token-sdk = { path = "zk-token-sdk", version = "=1.18.0" } solana_rbpf = "=0.8.0" +solana_utils = { path = "utils", version = "=1.18.0", default-features = false } spl-associated-token-account = "=2.3.1" spl-instruction-padding = "0.1" spl-memo = "=4.0.1" diff --git a/accounts-db/Cargo.toml b/accounts-db/Cargo.toml index 80559f5fb27821..879d3366197d26 100644 --- a/accounts-db/Cargo.toml +++ b/accounts-db/Cargo.toml @@ -35,7 +35,7 @@ num-traits = { workspace = true } num_cpus = { workspace = true } num_enum = { workspace = true } ouroboros = { workspace = true } -percentage = { workspace = true } +#percentage = { workspace = true } qualifier_attr = { workspace = true } rand = { workspace = true } rayon = { workspace = true } diff --git a/program-runtime/Cargo.toml b/program-runtime/Cargo.toml index ed4b2a60aa3f0a..b173df266d5ac3 100644 --- a/program-runtime/Cargo.toml +++ b/program-runtime/Cargo.toml @@ -19,7 +19,6 @@ libc = { workspace = true } log = { workspace = true } num-derive = { workspace = true } num-traits = { workspace = true } -percentage = { workspace = true } rand = { workspace = true } serde = { workspace = true, features = ["derive", "rc"] } solana-frozen-abi = { workspace = true } @@ -28,6 +27,7 @@ solana-measure = { workspace = true } solana-metrics = { workspace = true } solana-sdk = { workspace = true } solana_rbpf = { workspace = true } +solana_utils = { workspace = true } thiserror = { workspace = true } [dev-dependencies] diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 19f5f7486ea330..6a246bc7ccf829 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -4,7 +4,7 @@ use { timings::ExecuteDetailsTimings, }, log::{debug, error, log_enabled, trace}, - percentage::PercentageInteger, + solana_utils::percentage::PercentageInteger, rand::{thread_rng, Rng}, solana_measure::measure::Measure, solana_rbpf::{ diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b14ffab2076ca3..259a2b65fdb7af 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -39,7 +39,6 @@ num-traits = { workspace = true } num_cpus = { workspace = true } num_enum = { workspace = true } ouroboros = { workspace = true } -percentage = { workspace = true } qualifier_attr = { workspace = true } rand = { workspace = true } rayon = { workspace = true } @@ -66,6 +65,7 @@ solana-sdk = { workspace = true } solana-stake-program = { workspace = true } solana-svm = { workspace = true } solana-system-program = { workspace = true } +solana_utils = { workspace = true } solana-version = { workspace = true } solana-vote = { workspace = true } solana-vote-program = { workspace = true } diff --git a/streamer/Cargo.toml b/streamer/Cargo.toml index 8e1eb12dff1d42..15715840f5f778 100644 --- a/streamer/Cargo.toml +++ b/streamer/Cargo.toml @@ -21,7 +21,6 @@ libc = { workspace = true } log = { workspace = true } nix = { workspace = true } pem = { workspace = true } -percentage = { workspace = true } quinn = { workspace = true } quinn-proto = { workspace = true } rand = { workspace = true } @@ -29,6 +28,7 @@ rustls = { workspace = true, features = ["dangerous_configuration"] } solana-metrics = { workspace = true } solana-perf = { workspace = true } solana-sdk = { workspace = true } +solana_utils = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } x509-parser = { workspace = true } diff --git a/streamer/src/nonblocking/quic.rs b/streamer/src/nonblocking/quic.rs index 225412dd08b315..b0fb8851543a4c 100644 --- a/streamer/src/nonblocking/quic.rs +++ b/streamer/src/nonblocking/quic.rs @@ -13,7 +13,7 @@ use { bytes::Bytes, crossbeam_channel::Sender, indexmap::map::{Entry, IndexMap}, - percentage::Percentage, + solana_utils::percentage::Percentage, quinn::{Connecting, Connection, Endpoint, EndpointConfig, TokioRuntime, VarInt}, quinn_proto::VarIntBoundsExceeded, rand::{thread_rng, Rng}, diff --git a/streamer/src/nonblocking/stream_throttle.rs b/streamer/src/nonblocking/stream_throttle.rs index aa5e53aa1b156b..40ab7babd7df06 100644 --- a/streamer/src/nonblocking/stream_throttle.rs +++ b/streamer/src/nonblocking/stream_throttle.rs @@ -3,7 +3,7 @@ use { nonblocking::quic::ConnectionPeerType, quic::{StreamStats, MAX_UNSTAKED_CONNECTIONS}, }, - percentage::Percentage, + solana_utils::percentage::Percentage, std::{ cmp, sync::{ diff --git a/svm/Cargo.toml b/svm/Cargo.toml index 4fdf7d9cb1a0b4..b8e404f1a6e825 100644 --- a/svm/Cargo.toml +++ b/svm/Cargo.toml @@ -12,7 +12,6 @@ edition = { workspace = true } [dependencies] itertools = { workspace = true } log = { workspace = true } -percentage = { workspace = true } solana-accounts-db = { workspace = true } solana-bpf-loader-program = { workspace = true } solana-frozen-abi = { workspace = true } @@ -23,6 +22,7 @@ solana-metrics = { workspace = true } solana-program-runtime = { workspace = true } solana-sdk = { workspace = true } solana-system-program = { workspace = true } +solana_utils = { workspace = true } [lib] crate-type = ["lib"] diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index 71fc4e8e8a46b2..f27b1ed608a7b2 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -7,7 +7,7 @@ use { transaction_error_metrics::TransactionErrorMetrics, }, log::debug, - percentage::Percentage, + solana_utils::percentage::Percentage, solana_accounts_db::{ accounts::{LoadedTransaction, TransactionLoadResult}, transaction_results::{ diff --git a/utils/Cargo.toml b/utils/Cargo.toml new file mode 100644 index 00000000000000..dc49e5ac5565ee --- /dev/null +++ b/utils/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "solana_utils" +description = "Solana utils" +documentation = "https://docs.rs/solana-utils" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +clap = { workspace = true } +solana-version = { workspace = true } +num="0.2.0" + +[dev-dependencies] +solana-runtime = { workspace = true, features = ["dev-context-only-utils"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/utils/src/lib.rs b/utils/src/lib.rs new file mode 100644 index 00000000000000..ff6768906fb9bc --- /dev/null +++ b/utils/src/lib.rs @@ -0,0 +1 @@ +pub mod percentage; \ No newline at end of file diff --git a/utils/src/percentage.rs b/utils/src/percentage.rs new file mode 100644 index 00000000000000..0ede81d55e1d53 --- /dev/null +++ b/utils/src/percentage.rs @@ -0,0 +1,281 @@ +//! # Percentage +//! +//! `percentage` is a crate trying to make using percentages in a safer way and easier to debug. +//! Whenever you see a Percentage, you will know what is being calculated, instead of having to revise the code. +//! +//! # Example +//! +//! ``` +//! // You only need to import the `Percentage` struct +//! use percentage::Percentage; +//! +//! // Here we create the percentage to apply +//! let percent = Percentage::from(50); +//! +//! println!("{}", percent.value()); // Will print '50' +//! +//! // We can apply the percent to any number we want +//! assert_eq!(15, percent.apply_to(30)); +//! println!("50% of 30 is: {}", percent.apply_to(30)); // Will print '50% of 30 is: 15' +//! +//! // If you need to use floating points for the percent, you can use `from_decimal` instead +//! +//! let percent = Percentage::from_decimal(0.5); +//! assert_eq!(15.0, percent.apply_to(30.0)); +//! println!("50% of 30.0 is: {}", percent.apply_to(30.0)); // Will print '50% of 30.0 is: 15.0' +//! +//! ``` + +extern crate num; + +use num::{Num, NumCast}; + +pub struct PercentageInteger { + value: u8, +} + +pub struct PercentageDecimal { + value: f64, +} + +impl PercentageInteger { + /// Returns the percentage applied to the number given. + /// + /// # Arguments + /// + /// * `value` - The number to apply the percentage. + /// + /// # Examples + /// + /// ``` + /// use percentage::Percentage; + /// + /// let number = 90; + /// let percentage = Percentage::from(50); + /// + /// assert_eq!(45, percentage.apply_to(number)); + /// ``` + pub fn apply_to(&self, value: T) -> T { + (value * NumCast::from(self.value).unwrap()) / NumCast::from(100).unwrap() + } + + /// Returns the percentage saved. + /// + /// # Examples + /// + /// ``` + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from(50); + /// + /// assert_eq!(50, percentage.value()); + /// ``` + pub fn value(&self) -> u8 { + self.value + } +} + +impl PercentageDecimal { + /// Returns the percentage applied to the f64 given. + /// + /// # Arguments + /// + /// * `value` - The number to apply the percentage. + /// + /// # Examples + /// + /// ``` + /// use percentage::Percentage; + /// + /// let number = 90.0; + /// let percentage = Percentage::from_decimal(0.5); + /// + /// assert_eq!(45.0, percentage.apply_to(number)); + /// ``` + pub fn apply_to(&self, value: f64) -> f64 { + value * self.value + } + + /// Returns the percentage saved. + /// + /// # Examples + /// + /// ``` + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from_decimal(0.5); + /// + /// assert_eq!(0.5, percentage.value()); + /// ``` + pub fn value(&self) -> f64 { + self.value + } +} + +pub struct Percentage; + +impl Percentage { + /// Returns a new `PercentageInteger` with the Given value. + /// + /// # Arguments + /// + /// * `value` - The number to use as the percentage between 0 and 100. + /// + /// # Example + /// ``` + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from(50); + /// ``` + /// + /// # Panics + /// + /// Panics if `value` is over 100 + /// ```rust,should_panic + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from(150); + /// ``` + /// + /// Panics if `value` is below 0 + /// ```rust,should_panic + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from(-150); + /// ``` + pub fn from(value: T) -> PercentageInteger { + let value: u8 = NumCast::from(value) + .unwrap_or_else(|| panic!("Percentage value must be between 0 and 100")); + if value > 100 { + panic!("Percentage value must be between 0 and 100"); + } + PercentageInteger { value } + } + + /// Returns a new `PercentageDecimal` with the Given value. + /// + /// # Arguments + /// + /// * `value` - The number to use as the percentage between 0.0 and 1.0. + /// + /// # Example + /// ``` + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from_decimal(0.5); + /// ``` + /// + /// # Panics + /// + /// Panics if `value` is over 1.0 + /// ```rust,should_panic + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from_decimal(1.5); + /// ``` + /// + /// Panics if `value` is below 0 + /// ```rust,should_panic + /// use percentage::Percentage; + /// + /// let percentage = Percentage::from_decimal(-1.5); + /// ``` + pub fn from_decimal(value: f64) -> PercentageDecimal { + if value < 0.0 || value > 1.0 { + panic!("Percentage value must be between 0 and 1"); + } + PercentageDecimal { value } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + #[should_panic] + fn from_should_panic_if_value_is_over_100() { + Percentage::from(101); + } + #[test] + #[should_panic] + fn from_should_panic_if_value_is_below_0() { + Percentage::from(-1); + } + #[test] + fn from_should_save_value_on_u8_format() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15).value); + } + #[test] + fn from_should_save_value_from_i8_or_u8() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15 as i8).value); + assert_eq!(test, Percentage::from(15 as u8).value); + } + #[test] + fn from_should_save_value_from_i16_or_u16() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15 as i16).value); + assert_eq!(test, Percentage::from(15 as u16).value); + } + #[test] + fn from_should_save_value_from_i32_or_u32() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15 as i32).value); + assert_eq!(test, Percentage::from(15 as u32).value); + } + #[test] + fn from_should_save_value_from_i64_or_u64() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15 as i64).value); + assert_eq!(test, Percentage::from(15 as u64).value); + } + #[test] + fn from_should_save_value_from_i128_or_u128() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15 as i128).value); + assert_eq!(test, Percentage::from(15 as u128).value); + } + #[test] + fn from_should_save_value_from_isize_or_usize() { + let test: u8 = 15; + assert_eq!(test, Percentage::from(15 as isize).value); + assert_eq!(test, Percentage::from(15 as usize).value); + } + + #[test] + #[should_panic] + fn from_decimal_should_panic_if_value_is_over_1() { + Percentage::from_decimal(1.1); + } + #[test] + #[should_panic] + fn from_decimal_should_panic_if_value_is_below_0() { + Percentage::from_decimal(-1.1); + } + #[test] + fn from_decimal_should_save_value_from_f64() { + let test: f64 = 0.34567; + assert_eq!(test, Percentage::from_decimal(0.34567 as f64).value); + } + + #[test] + fn value_should_return_decimal_percentage_on_f64() { + let test: f64 = 0.34; + assert_eq!(test, Percentage::from_decimal(0.34).value()); + } + #[test] + fn value_should_return_integer_percentage_on_u8() { + let test: u8 = 34; + assert_eq!(test, Percentage::from(34 as i32).value()); + } + + #[test] + fn apply_to_should_return_the_value_with_the_percentage_applied_with_the_same_type() { + let number = 100; + assert_eq!(84, Percentage::from(84).apply_to(number)); + assert_eq!(84 as i32, Percentage::from(84).apply_to(number as i32)); + assert_eq!(84 as u32, Percentage::from(84).apply_to(number as u32)); + assert_eq!(84.0, Percentage::from_decimal(0.84).apply_to(number as f64)); + } +}