diff --git a/Gemfile b/Gemfile index 312a3c1..c6c34a8 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,7 @@ gem 'helix-rails' gem "cash_flow_c", path: "./cash_flow_c/" gem 'cash_flow_rust_helix', path: 'crates/cash_flow_rust_helix' gem 'cash_flow_rust_rutie', path: 'crates/cash_flow_rust_rutie' +gem 'cash_flow_rust_magnus', path: 'crates/cash_flow_rust_magnus' gem 'ffi' # for cash_flow_rust_ffi gem 'fiddle' # for cash_flow_rust_ruru diff --git a/Gemfile.lock b/Gemfile.lock index 3f97ff0..b78687f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,6 +9,12 @@ PATH cash_flow_rust_helix (1.0.0) helix_runtime (~> 0.7.5) +PATH + remote: crates/cash_flow_rust_magnus + specs: + cash_flow_rust_magnus (0.1.0) + rake (> 1) + PATH remote: crates/cash_flow_rust_rutie specs: @@ -149,6 +155,7 @@ DEPENDENCIES benchmark-ips cash_flow_c! cash_flow_rust_helix! + cash_flow_rust_magnus! cash_flow_rust_rutie! ffi fiddle diff --git a/benchmark.rb b/benchmark.rb index c5267c5..4400188 100644 --- a/benchmark.rb +++ b/benchmark.rb @@ -61,6 +61,13 @@ def cash_flow(cash_in, cash_out, reinvest_rate, term, year) x.report("rust rutie class") { RustRutie.cash_flow(10000.0, 800.0, 50.0, 30, 5) } + # ======================================== + # Rust using magnus class method + # ======================================== + require 'cash_flow_rust_magnus' + raise 'rust magnus class method fail' unless cash_flow(10000, 800, 50, 30, 5) == RustMagnus.cash_flow(10000, 800, 50, 30, 5) + + x.report("rust magnus class") { RustMagnus.cash_flow(10000, 800, 50, 30, 5) } # ==================================== # C using standard Ruby extension diff --git a/build-libs.sh b/build-libs.sh index 1233901..ea2fd72 100755 --- a/build-libs.sh +++ b/build-libs.sh @@ -22,3 +22,7 @@ bundle install --quiet cd $base_dir cd crates/cash_flow_rust_rutie bin/setup + +cd $base_dir +cd crates/cash_flow_rust_magnus +rake install diff --git a/crates/cash_flow_rust_magnus/Rakefile b/crates/cash_flow_rust_magnus/Rakefile new file mode 100644 index 0000000..87dcb18 --- /dev/null +++ b/crates/cash_flow_rust_magnus/Rakefile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "rake/testtask" +load File.expand_path("./ext/cash_flow_rust_magnus/Rakefile", __dir__) + +task default: :test + +Rake::TestTask.new do |t| + t.deps << :dev << :install + t.test_files = FileList[File.expand_path("test/*_test.rb", __dir__)] +end diff --git a/crates/cash_flow_rust_magnus/cash_flow_rust_magnus.gemspec b/crates/cash_flow_rust_magnus/cash_flow_rust_magnus.gemspec new file mode 100644 index 0000000..0e93e4b --- /dev/null +++ b/crates/cash_flow_rust_magnus/cash_flow_rust_magnus.gemspec @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +Gem::Specification.new do |spec| + spec.name = "cash_flow_rust_magnus" + spec.version = "0.1.0" + spec.summary = "-" + spec.files = Dir["lib/**/*.rb"].concat(Dir["ext/cash_flow_rust_magnus/src/**/*.rs"]) << "ext/cash_flow_rust_magnus/Cargo.toml" << "ext/cash_flow_rust_magnus/Cargo.lock" << "ext/cash_flow_rust_magnus/Rakefile" + spec.extensions = ["ext/cash_flow_rust_magnus/Rakefile"] + spec.authors = ["Mat Sadler"] + spec.license = "MIT" + + spec.requirements = ["Rust >= 1.51.0"] + + spec.add_runtime_dependency "rake", "> 1" +end diff --git a/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Cargo.lock b/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Cargo.lock new file mode 100644 index 0000000..c53770b --- /dev/null +++ b/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Cargo.lock @@ -0,0 +1,431 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cash_flow_rust_magnus" +version = "0.1.0" +dependencies = [ + "magnus", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "darling" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e92cb285610dd935f60ee8b4d62dd1988bd12b7ea50579bd6a138201525318e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c29e95ab498b18131ea460b2c0baa18cbf041231d122b0b7bfebef8c8e88989" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b21dd6b221dd547528bd6fb15f1a3b7ab03b9a06f76bff288a8c629bcfbe7f0e" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "magnus" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaae8b7e44b41a392928928930e8c3142ebc4facfe41ba3915b4648420deee1" +dependencies = [ + "bindgen", + "magnus-macros", +] + +[[package]] +name = "magnus-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27968fcabe2ef7e35b8331d71a62882282996f6222c133c0255cf7f33b8d9b48" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Cargo.toml b/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Cargo.toml new file mode 100644 index 0000000..ddc8361 --- /dev/null +++ b/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cash_flow_rust_magnus" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +magnus = "0.3.0" diff --git a/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Rakefile b/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Rakefile new file mode 100644 index 0000000..63529ef --- /dev/null +++ b/crates/cash_flow_rust_magnus/ext/cash_flow_rust_magnus/Rakefile @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require "shellwords" + +class RakeCargoHelper + attr_reader :gemname + + def initialize(gemname=File.basename(__dir__)) + @gemname = gemname + end + + def self.command?(name) + exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""] + ENV["PATH"].split(File::PATH_SEPARATOR).any? do |path| + exts.any? do |ext| + exe = File.join(path, "#{name}#{ext}") + File.executable?(exe) && !File.directory?(exe) + end + end + end + + def self.rust_toolchain + str = `rustc --version --verbose` + info = str.lines.map {|l| l.chomp.split(/:\s+/, 2)}.drop(1).to_h + info["host"] + end + + def self.cargo_target_dir + return @cargo_target_dir if defined? @cargo_target_dir + + str = `cargo metadata --format-version 1 --offline --no-deps --quiet` + begin + require "json" + dir = JSON.parse(str)["target_directory"] + rescue LoadError # json is usually part of the stdlib, but just in case + /"target_directory"\s*:\s*"(?