From eb3b669f6b7ede1b6421d9ab50ff72920431fc32 Mon Sep 17 00:00:00 2001 From: Chun-Min Chang Date: Wed, 26 Feb 2020 15:25:36 +0000 Subject: [PATCH] Bug 1614971 - Update cubeb-coreaudio to 4acd802. r=padenot Pick commits: - 4acd802: Destroy the stream properly - 54d950a: Run the tests in the subcrate (#51) - 132d209: Clean up clippy warnings and errors (#49) Differential Revision: https://phabricator.services.mozilla.com/D63725 --HG-- extra : moz-landing-system : lando --- .cargo/config.in | 2 +- Cargo.lock | 14 +- .../coreaudio-sys-utils/.cargo-checksum.json | 2 +- .../coreaudio-sys-utils/src/audio_unit.rs | 30 +- .../rust/cubeb-coreaudio/.cargo-checksum.json | 2 +- third_party/rust/cubeb-coreaudio/.travis.yml | 3 + third_party/rust/cubeb-coreaudio/Cargo.toml | 1 + .../cubeb-coreaudio/install_rustfmt_clippy.sh | 17 ++ .../rust/cubeb-coreaudio/run_sanitizers.sh | 20 ++ third_party/rust/cubeb-coreaudio/run_tests.sh | 23 ++ .../src/backend/aggregate_device.rs | 5 +- .../src/backend/device_property.rs | 3 +- .../rust/cubeb-coreaudio/src/backend/mixer.rs | 32 +- .../rust/cubeb-coreaudio/src/backend/mod.rs | 78 +++-- .../cubeb-coreaudio/src/backend/tests/api.rs | 4 +- .../src/backend/tests/device_change.rs | 1 + .../src/backend/tests/utils.rs | 3 +- .../rust/cubeb-coreaudio/src/backend/utils.rs | 14 +- third_party/rust/cubeb-coreaudio/src/capi.rs | 4 +- third_party/rust/cubeb-coreaudio/src/lib.rs | 2 + .../rust/float-cmp/.cargo-checksum.json | 1 + third_party/rust/float-cmp/Cargo.toml | 37 +++ third_party/rust/float-cmp/LICENSE | 19 ++ third_party/rust/float-cmp/README.md | 143 +++++++++ third_party/rust/float-cmp/src/eq.rs | 284 ++++++++++++++++++ third_party/rust/float-cmp/src/lib.rs | 196 ++++++++++++ third_party/rust/float-cmp/src/macros.rs | 77 +++++ third_party/rust/float-cmp/src/ratio.rs | 142 +++++++++ third_party/rust/float-cmp/src/ulps.rs | 240 +++++++++++++++ third_party/rust/float-cmp/src/ulps_eq.rs | 119 ++++++++ toolkit/library/rust/shared/Cargo.toml | 2 +- 31 files changed, 1454 insertions(+), 66 deletions(-) create mode 100644 third_party/rust/cubeb-coreaudio/install_rustfmt_clippy.sh create mode 100644 third_party/rust/float-cmp/.cargo-checksum.json create mode 100644 third_party/rust/float-cmp/Cargo.toml create mode 100644 third_party/rust/float-cmp/LICENSE create mode 100644 third_party/rust/float-cmp/README.md create mode 100644 third_party/rust/float-cmp/src/eq.rs create mode 100644 third_party/rust/float-cmp/src/lib.rs create mode 100644 third_party/rust/float-cmp/src/macros.rs create mode 100644 third_party/rust/float-cmp/src/ratio.rs create mode 100644 third_party/rust/float-cmp/src/ulps.rs create mode 100644 third_party/rust/float-cmp/src/ulps_eq.rs diff --git a/.cargo/config.in b/.cargo/config.in index 90882e3bbe3b..4d0cb3cba169 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -70,7 +70,7 @@ rev = "5e870faf6f95d79d11efc813e56370ad124bbed5" [source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"] git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs" replace-with = "vendored-sources" -rev = "7fe03b4201160f84aad1c12536affe6b05f8839f" +rev = "4acd80233efa645ac79769f37b07d495c1b42070" [source.crates-io] replace-with = "vendored-sources" diff --git a/Cargo.lock b/Cargo.lock index e8605f8cd540..632c2cf42379 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -678,7 +678,7 @@ dependencies = [ [[package]] name = "coreaudio-sys-utils" version = "0.1.0" -source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=7fe03b4201160f84aad1c12536affe6b05f8839f#7fe03b4201160f84aad1c12536affe6b05f8839f" +source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=4acd80233efa645ac79769f37b07d495c1b42070#4acd80233efa645ac79769f37b07d495c1b42070" dependencies = [ "core-foundation-sys", "coreaudio-sys", @@ -915,13 +915,14 @@ dependencies = [ [[package]] name = "cubeb-coreaudio" version = "0.1.0" -source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=7fe03b4201160f84aad1c12536affe6b05f8839f#7fe03b4201160f84aad1c12536affe6b05f8839f" +source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=4acd80233efa645ac79769f37b07d495c1b42070#4acd80233efa645ac79769f37b07d495c1b42070" dependencies = [ "atomic", "audio-mixer", "bitflags", "coreaudio-sys-utils", "cubeb-backend", + "float-cmp", "lazy_static", "libc", "mach", @@ -1260,6 +1261,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d" +dependencies = [ + "num-traits", +] + [[package]] name = "fluent-langneg" version = "0.12.1" diff --git a/third_party/rust/coreaudio-sys-utils/.cargo-checksum.json b/third_party/rust/coreaudio-sys-utils/.cargo-checksum.json index 93431db90856..f121623d3ada 100644 --- a/third_party/rust/coreaudio-sys-utils/.cargo-checksum.json +++ b/third_party/rust/coreaudio-sys-utils/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"35acbd2f8633a6109f3d3e554bef8d847c049ce6ef7a5f570468819e41344d7f","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"df10160d9fd83a2c23a49e69b78d39db3a9d6389607df6acfc05821293b6af5f","src/audio_unit.rs":"bc743a1b8033ab5459520c75d7f5230d24cda5ea1198a5b4e1594256af308f47","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"195ca94cbc61948637bfdcbe22070a1e6d41e97cec22301df4e45dcef7b1c208","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"35acbd2f8633a6109f3d3e554bef8d847c049ce6ef7a5f570468819e41344d7f","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"df10160d9fd83a2c23a49e69b78d39db3a9d6389607df6acfc05821293b6af5f","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"195ca94cbc61948637bfdcbe22070a1e6d41e97cec22301df4e45dcef7b1c208","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null} \ No newline at end of file diff --git a/third_party/rust/coreaudio-sys-utils/src/audio_unit.rs b/third_party/rust/coreaudio-sys-utils/src/audio_unit.rs index e485d4a98264..059a58f26b6a 100644 --- a/third_party/rust/coreaudio-sys-utils/src/audio_unit.rs +++ b/third_party/rust/coreaudio-sys-utils/src/audio_unit.rs @@ -1,23 +1,26 @@ use coreaudio_sys::*; +use std::convert::TryFrom; use std::os::raw::c_void; +use std::ptr; pub fn audio_unit_get_property_info( unit: AudioUnit, property: AudioUnitPropertyID, scope: AudioUnitScope, element: AudioUnitElement, - size: *mut usize, - writable: *mut Boolean, + size: &mut usize, + writable: Option<&mut bool>, // Use `Option` since `writable` is nullable. ) -> OSStatus { assert!(!unit.is_null()); + assert!(UInt32::try_from(*size).is_ok()); // Check if `size` can be converted to a UInt32. unsafe { AudioUnitGetPropertyInfo( unit, property, scope, element, - size as *mut UInt32, - writable, + size as *mut usize as *mut UInt32, + writable.map_or(ptr::null_mut(), |v| v as *mut bool as *mut Boolean), ) } } @@ -27,18 +30,19 @@ pub fn audio_unit_get_property( property: AudioUnitPropertyID, scope: AudioUnitScope, element: AudioUnitElement, - data: *mut T, - size: *mut usize, + data: &mut T, + size: &mut usize, ) -> OSStatus { assert!(!unit.is_null()); + assert!(UInt32::try_from(*size).is_ok()); // Check if `size` can be converted to a UInt32. unsafe { AudioUnitGetProperty( unit, property, scope, element, - data as *mut c_void, - size as *mut UInt32, + data as *mut T as *mut c_void, + size as *mut usize as *mut UInt32, ) } } @@ -48,7 +52,7 @@ pub fn audio_unit_set_property( property: AudioUnitPropertyID, scope: AudioUnitScope, element: AudioUnitElement, - data: *const T, + data: &T, size: usize, ) -> OSStatus { assert!(!unit.is_null()); @@ -58,7 +62,7 @@ pub fn audio_unit_set_property( property, scope, element, - data as *const c_void, + data as *const T as *const c_void, size as UInt32, ) } @@ -121,11 +125,11 @@ pub fn audio_output_unit_stop(unit: AudioUnit) -> OSStatus { pub fn audio_unit_render( in_unit: AudioUnit, - io_action_flags: *mut AudioUnitRenderActionFlags, - in_time_stamp: *const AudioTimeStamp, + io_action_flags: &mut AudioUnitRenderActionFlags, + in_time_stamp: &AudioTimeStamp, in_output_bus_number: u32, in_number_frames: u32, - io_data: *mut AudioBufferList, + io_data: &mut AudioBufferList, ) -> OSStatus { assert!(!in_unit.is_null()); unsafe { diff --git a/third_party/rust/cubeb-coreaudio/.cargo-checksum.json b/third_party/rust/cubeb-coreaudio/.cargo-checksum.json index 9853f754caca..1558663fd9e7 100644 --- a/third_party/rust/cubeb-coreaudio/.cargo-checksum.json +++ b/third_party/rust/cubeb-coreaudio/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"71e11635d066577025bc5c4a37bf4257f74fef3ae9ee82884b4a2ef73a55f639","Cargo.toml":"6215c23a25515907eabb24a265c192458f93836b6721ab1282a2e43201230714","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"fa323b7386a8a0c75478b77a30a3c5d33f1df23d9775b97570fa0501ef784e95","run_sanitizers.sh":"6d0eb16ec37d7808b7efe862426f7b8805985da31074f645594723f48a2f003c","run_tests.sh":"c764a89fe2a6b7ccdeb01895d29b2017c5b76f468aff456050babd66c1472687","src/backend/aggregate_device.rs":"7cd732f3e1e71876753515b26ee69a315414e58869216e99ff95c6828408c4db","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"1b066b48ed09026a9286b1b8f40e2720854c3410b0f02c795a580792fda34ea9","src/backend/mixer.rs":"6887f90867d0f47a6cd14b3b77b3a0050beb6cf0e69447ec34719871ffca5e9b","src/backend/mod.rs":"3a4c6a953d312298d669a3fbf32d918e72e8e6836ab78775cd71530e4d35e7b4","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"0c78b255921a5c13310132134bb194fc6d9c6fbe90fd05624b82b552711c5a8a","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8533df1357c154922200a706bbb21d20007fd3a343d9c47697314f91c1c6a281","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"b74f10216bd02d5336f46db371c9c8e1a648deead84068d9397eca03986aae1e","src/backend/utils.rs":"ee77bc266d672d3d9e23eb3290c1f897687394c6e459338804a17433380a6fd2","src/capi.rs":"61f8f0c4373adaefba1eb6e7084687e83a10136db96438bc35884327668e411f","src/lib.rs":"1ff4b738ed194061fca4ff745f847dea4de4e7a4fa1f898e7b4ad5e70c62386d","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null} \ No newline at end of file +{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"878d9519da0844cd3de2999f81fce966452f0fb65150605c25a1abf3523360ab","Cargo.toml":"bcb3ec3785c3cbe799bb41c07176afdd0028328be79932a4e44bc97a364e8c69","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"fa323b7386a8a0c75478b77a30a3c5d33f1df23d9775b97570fa0501ef784e95","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_sanitizers.sh":"913efec447b4c37751af93eef4a974f293426c20c720d37c5bbe69e955d105eb","run_tests.sh":"fcdae57b81426c5dbfc8873a9ff1ac07924ed0717f822b50a67934cac2e9d4f1","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"324a7c9672daa49ee2221a56d140e1c8298719dab66f204b19d10f3632f96602","src/backend/mixer.rs":"0c237bd5ca63b028c4b22ddc5bc026d7e21c0fa9b4e337f00b6131ed0a0806a5","src/backend/mod.rs":"250cf24aabe1f1e9e8eafd375a538554523accaa6b514588b157d0c413d39b86","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"d81128f0982ecc816666e91f57e722cc21ba9cc09a2a36103acc7c981f57b36d","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"2138e7ed4721872ce3a41f3652bfa4e7eca97fd136473af6472313c61ff24ed3","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null} \ No newline at end of file diff --git a/third_party/rust/cubeb-coreaudio/.travis.yml b/third_party/rust/cubeb-coreaudio/.travis.yml index aea09f182d77..9fe1f38ecd75 100644 --- a/third_party/rust/cubeb-coreaudio/.travis.yml +++ b/third_party/rust/cubeb-coreaudio/.travis.yml @@ -5,9 +5,12 @@ rust: - nightly os: - osx +env: + - RUST_BACKTRACE=1 before_script: - rustc --version - cargo --version + - sh install_rustfmt_clippy.sh script: - cargo build --verbose - sh run_tests.sh diff --git a/third_party/rust/cubeb-coreaudio/Cargo.toml b/third_party/rust/cubeb-coreaudio/Cargo.toml index 251139742256..8d9d958228ef 100644 --- a/third_party/rust/cubeb-coreaudio/Cargo.toml +++ b/third_party/rust/cubeb-coreaudio/Cargo.toml @@ -12,6 +12,7 @@ atomic = "0.4" bitflags = "1.0" coreaudio-sys-utils = { path = "coreaudio-sys-utils" } cubeb-backend = "0.6" +float-cmp = "0.6" libc = "0.2" lazy_static = "1.2" mach = "0.3" diff --git a/third_party/rust/cubeb-coreaudio/install_rustfmt_clippy.sh b/third_party/rust/cubeb-coreaudio/install_rustfmt_clippy.sh new file mode 100644 index 000000000000..5063059c6038 --- /dev/null +++ b/third_party/rust/cubeb-coreaudio/install_rustfmt_clippy.sh @@ -0,0 +1,17 @@ +# https://github.com/rust-lang/rustup-components-history/blob/master/README.md +# https://github.com/rust-lang/rust-clippy/blob/27acea0a1baac6cf3ac6debfdbce04f91e15d772/.travis.yml#L40-L46 +if ! rustup component add rustfmt; then + TARGET=$(rustc -Vv | awk '/host/{print $2}') + NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/rustfmt") + curl -sSL "https://static.rust-lang.org/dist/${NIGHTLY}/rustfmt-nightly-${TARGET}.tar.xz" | \ + tar -xJf - --strip-components=3 -C ~/.cargo/bin + rm -rf ~/.cargo/bin/doc +fi + +if ! rustup component add clippy; then + TARGET=$(rustc -Vv | awk '/host/{print $2}') + NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/clippy") + rustup default nightly-${NIGHTLY} + rustup component add rustfmt + rustup component add clippy +fi diff --git a/third_party/rust/cubeb-coreaudio/run_sanitizers.sh b/third_party/rust/cubeb-coreaudio/run_sanitizers.sh index bd9c233c252c..dd7036abbfab 100644 --- a/third_party/rust/cubeb-coreaudio/run_sanitizers.sh +++ b/third_party/rust/cubeb-coreaudio/run_sanitizers.sh @@ -14,6 +14,26 @@ else exit fi +# Run tests in the sub crate +# ------------------------------------------------------------------------------------------------- +cd coreaudio-sys-utils + +echo "\n\nRun ASan\n-----------\n" +RUSTFLAGS="-Z sanitizer=address" cargo test + +echo "\n\nRun LSan\n-----------\n" +RUSTFLAGS="-Z sanitizer=leak" cargo test + +echo "\n\nRun MSan\n-----------\n" +RUSTFLAGS="-Z sanitizer=memory" cargo test + +echo "\n\nRun TSan\n-----------\n" +RUSTFLAGS="-Z sanitizer=thread" cargo test + +cd .. + +# Run tests in the main crate +# ------------------------------------------------------------------------------------------------- echo "\n\nRun ASan\n-----------\n" RUSTFLAGS="-Z sanitizer=address" sh run_tests.sh diff --git a/third_party/rust/cubeb-coreaudio/run_tests.sh b/third_party/rust/cubeb-coreaudio/run_tests.sh index dacde7cfa742..8854ebab87be 100644 --- a/third_party/rust/cubeb-coreaudio/run_tests.sh +++ b/third_party/rust/cubeb-coreaudio/run_tests.sh @@ -1,6 +1,29 @@ # display backtrace for debugging export RUST_BACKTRACE=1 +# Run tests in the sub crate +# ------------------------------------------------------------------------------------------------- +cd coreaudio-sys-utils + +# Format check +cargo fmt --all -- --check + +# Lints check +cargo clippy -- -D warnings + +# Regular Tests +cargo test + +cd .. + +# Run tests in the main crate +# ------------------------------------------------------------------------------------------------- +# Format check +cargo fmt --all -- --check + +# Lints check +cargo clippy -- -D warnings + # Regular Tests cargo test --verbose cargo test test_configure_output -- --ignored diff --git a/third_party/rust/cubeb-coreaudio/src/backend/aggregate_device.rs b/third_party/rust/cubeb-coreaudio/src/backend/aggregate_device.rs index a3b43f18df1f..c98bfce3595c 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/aggregate_device.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/aggregate_device.rs @@ -242,15 +242,14 @@ impl AggregateDevice { CFRelease(device_stacked_key as *const c_void); // This call will fire `audiounit_collection_changed_callback` indirectly! - let status = audio_object_get_property_data_with_qualifier( + audio_object_get_property_data_with_qualifier( plugin_id, &address, mem::size_of_val(&device_dict), &device_dict, &mut size, &mut device_id, - ); - status + ) }; if status == NO_ERR { assert_ne!(device_id, kAudioObjectUnknown); diff --git a/third_party/rust/cubeb-coreaudio/src/backend/device_property.rs b/third_party/rust/cubeb-coreaudio/src/backend/device_property.rs index 023ecd41aa60..2dcca4056e09 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/device_property.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/device_property.rs @@ -83,7 +83,7 @@ pub fn get_device_label( id: AudioDeviceID, devtype: DeviceType, ) -> std::result::Result { - get_device_source_name(id, devtype).or(get_device_name(id, devtype)) + get_device_source_name(id, devtype).or_else(|_| get_device_name(id, devtype)) } pub fn get_device_manufacturer( @@ -217,6 +217,7 @@ pub fn get_device_stream_format( } } +#[allow(clippy::cast_ptr_alignment)] // Allow casting *mut u8 to *mut AudioBufferList pub fn get_device_stream_configuration( id: AudioDeviceID, devtype: DeviceType, diff --git a/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs b/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs index e7baa2f1588f..d01e2669a0bb 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/mixer.rs @@ -44,13 +44,17 @@ pub fn get_channel_order(channel_layout: ChannelLayout) -> Vec Vec { assert_ne!(channel_count, 0); let mut channels = Vec::with_capacity(channel_count); - for i in 0..channel_count { - channels.push(if i < CHANNEL_OERDER.len() { - CHANNEL_OERDER[i] - } else { - audio_mixer::Channel::Silence - }); + for channel in CHANNEL_OERDER.iter().take(channel_count) { + channels.push(*channel); } + + if channel_count > CHANNEL_OERDER.len() { + channels.extend(vec![ + audio_mixer::Channel::Silence; + channel_count - CHANNEL_OERDER.len() + ]); + } + channels } @@ -202,7 +206,7 @@ impl Mixer { } let all_silence = vec![audio_mixer::Channel::Silence; out_channel_count]; - if output_channels.len() == 0 + if output_channels.is_empty() || out_channel_count != output_channels.len() || all_silence == output_channels { @@ -430,3 +434,17 @@ fn test_get_channel_order() { ] ); } + +#[test] +fn test_get_default_channel_order() { + for len in 1..CHANNEL_OERDER.len() + 10 { + let channels = get_default_channel_order(len); + if len <= CHANNEL_OERDER.len() { + assert_eq!(channels, &CHANNEL_OERDER[..len]); + } else { + let silences = vec![audio_mixer::Channel::Silence; len - CHANNEL_OERDER.len()]; + assert_eq!(channels[..CHANNEL_OERDER.len()], CHANNEL_OERDER); + assert_eq!(&channels[CHANNEL_OERDER.len()..], silences.as_slice()); + } + } +} diff --git a/third_party/rust/cubeb-coreaudio/src/backend/mod.rs b/third_party/rust/cubeb-coreaudio/src/backend/mod.rs index 2cdbea3253bf..395551339daa 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/mod.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/mod.rs @@ -194,7 +194,7 @@ fn create_device_info(id: AudioDeviceID, devtype: DeviceType) -> Result device_flags::DEV_INPUT, DeviceType::OUTPUT => device_flags::DEV_OUTPUT, @@ -328,9 +328,9 @@ fn get_volume(unit: AudioUnit) -> Result { } fn minimum_resampling_input_frames(input_rate: f64, output_rate: f64, output_frames: i64) -> i64 { - assert_ne!(input_rate, 0_f64); - assert_ne!(output_rate, 0_f64); - if input_rate == output_rate { + assert!(!approx_eq!(f64, input_rate, 0_f64)); + assert!(!approx_eq!(f64, output_rate, 0_f64)); + if approx_eq!(f64, input_rate, output_rate) { return output_frames; } (input_rate * output_frames as f64 / output_rate).ceil() as i64 @@ -383,6 +383,12 @@ extern "C" fn audiounit_input_callback( user_ptr as *const AudioUnitStream ); + // `flags` and `tstamp` must be non-null so they can be casted into the references. + assert!(!flags.is_null()); + let flags = unsafe { &mut (*flags) }; + assert!(!tstamp.is_null()); + let tstamp = unsafe { &(*tstamp) }; + // Create the AudioBufferList to store input. let mut input_buffer_list = AudioBufferList::default(); input_buffer_list.mBuffers[0].mDataByteSize = @@ -533,21 +539,20 @@ extern "C" fn audiounit_input_callback( } } - let status = match handle { + match handle { ErrorHandle::Reinit => { stm.reinit_async(); NO_ERR } ErrorHandle::Return(s) => s, - }; - status + } } fn host_time_to_ns(host_time: u64) -> u64 { let mut rv: f64 = host_time as f64; rv *= HOST_TIME_TO_NS_RATIO.0 as f64; rv /= HOST_TIME_TO_NS_RATIO.1 as f64; - return rv as u64; + rv as u64 } fn compute_output_latency(stm: &AudioUnitStream, host_time: u64) -> u32 { @@ -748,8 +753,8 @@ extern "C" fn audiounit_output_callback( ) }; let start = frames_to_bytes(outframes as usize); - for i in start..out_bytes.len() { - out_bytes[i] = 0; + for byte in out_bytes.iter_mut().skip(start) { + *byte = 0; } } @@ -926,7 +931,7 @@ fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> Vec Vec Result<()> { let current_frames = get_buffer_size(unit, devtype).map_err(|e| { cubeb_log!( @@ -1392,7 +1398,7 @@ fn get_range_of_sample_rates( ) -> std::result::Result<(f64, f64), String> { let result = get_ranges_of_device_sample_rate(devid, devtype); if let Err(e) = result { - return Err(format!("status {}", e).to_string()); + return Err(format!("status {}", e)); } let rates = result.unwrap(); if rates.is_empty() { @@ -1766,9 +1772,7 @@ impl DevicesData { } fn is_empty(&self) -> bool { - self.changed_callback == None - && self.callback_user_ptr == ptr::null_mut() - && self.devices.is_empty() + self.changed_callback == None && self.callback_user_ptr.is_null() && self.devices.is_empty() } } @@ -2138,7 +2142,7 @@ impl ContextOps for AudioUnitContext { cubeb_log!("Fail to create device info for input."); e })?; - let stm_params = StreamParams::from(unsafe { (*params.as_ptr()) }); + let stm_params = StreamParams::from(unsafe { *params.as_ptr() }); Some((stm_params, in_device)) } else { None @@ -2150,7 +2154,7 @@ impl ContextOps for AudioUnitContext { cubeb_log!("Fail to create device info for output."); e })?; - let stm_params = StreamParams::from(unsafe { (*params.as_ptr()) }); + let stm_params = StreamParams::from(unsafe { *params.as_ptr() }); Some((stm_params, out_device)) } else { None @@ -2391,6 +2395,7 @@ impl<'ctx> CoreStreamData<'ctx> { && !is_device_a_type_of(self.output_device.id, DeviceType::INPUT) } + #[allow(clippy::cognitive_complexity)] // TODO: Refactoring. fn setup(&mut self) -> Result<()> { if self .input_stream_params @@ -2756,14 +2761,14 @@ impl<'ctx> CoreStreamData<'ctx> { }; let resampler_input_params = if self.has_input() { - let mut params = unsafe { (*(self.input_stream_params.as_ptr())) }; + let mut params = unsafe { *(self.input_stream_params.as_ptr()) }; params.rate = self.input_hw_rate as u32; Some(params) } else { None }; let resampler_output_params = if self.has_output() { - let params = unsafe { (*(self.output_stream_params.as_ptr())) }; + let params = unsafe { *(self.output_stream_params.as_ptr()) }; Some(params) } else { None @@ -3061,6 +3066,8 @@ impl<'ctx> Drop for CoreStreamData<'ctx> { // #[repr(C)] is used to prevent any padding from being added in the beginning of the AudioUnitStream. #[repr(C)] #[derive(Debug)] +// Allow exposing this private struct in public interfaces when running tests. +#[cfg_attr(test, allow(private_in_public))] struct AudioUnitStream<'ctx> { context: &'ctx mut AudioUnitContext, user_ptr: *mut c_void, @@ -3231,8 +3238,8 @@ impl<'ctx> AudioUnitStream<'ctx> { } } - if vol_rv.is_ok() { - set_volume(self.core_stream_data.output_unit, vol_rv.unwrap()); + if let Ok(volume) = vol_rv { + set_volume(self.core_stream_data.output_unit, volume); } // If the stream was running, start it again. @@ -3295,6 +3302,29 @@ impl<'ctx> AudioUnitStream<'ctx> { } fn destroy(&mut self) { + if self + .core_stream_data + .uninstall_system_changed_callback() + .is_err() + { + cubeb_log!( + "({:p}) Could not uninstall the system changed callback", + self as *const AudioUnitStream + ); + } + + if self + .core_stream_data + .uninstall_device_changed_callback() + .is_err() + { + cubeb_log!( + "({:p}) Could not uninstall all device change listeners", + self as *const AudioUnitStream + ); + } + + // Execute the stream destroy work. self.destroy_pending.store(true, Ordering::SeqCst); let queue = self.context.serial_queue; @@ -3407,10 +3437,10 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> { let mut device: Box = Box::new(ffi::cubeb_device::default()); - let input_name = input_name.unwrap_or(CString::default()); + let input_name = input_name.unwrap_or_default(); device.input_name = input_name.into_raw(); - let output_name = output_name.unwrap_or(CString::default()); + let output_name = output_name.unwrap_or_default(); device.output_name = output_name.into_raw(); Ok(unsafe { DeviceRef::from_ptr(Box::into_raw(device)) }) diff --git a/third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs b/third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs index 4164f7540ec6..da26f500a0a5 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/tests/api.rs @@ -730,7 +730,7 @@ fn test_get_preferred_channel_layout_output() { ("ispk", STEREO.to_vec()), ("FApd", STEREO.to_vec()), ] - .into_iter() + .iter() .cloned() .collect(); @@ -766,7 +766,7 @@ fn test_get_current_channel_layout_output() { ("ispk", STEREO.to_vec()), ("FApd", STEREO.to_vec()), ] - .into_iter() + .iter() .cloned() .collect(); diff --git a/third_party/rust/cubeb-coreaudio/src/backend/tests/device_change.rs b/third_party/rust/cubeb-coreaudio/src/backend/tests/device_change.rs index 9ac543f4304b..5f08bcad3527 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/tests/device_change.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/tests/device_change.rs @@ -337,6 +337,7 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ if !run_available { println!("No enough devices to run the test!"); + return; } let changed_count = Arc::new(Mutex::new(0u32)); diff --git a/third_party/rust/cubeb-coreaudio/src/backend/tests/utils.rs b/third_party/rust/cubeb-coreaudio/src/backend/tests/utils.rs index 411b931af012..98cb17e7b4dd 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/tests/utils.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/tests/utils.rs @@ -503,6 +503,7 @@ pub fn test_audiounit_scope_is_enabled(unit: AudioUnit, scope: Scope) -> bool { Scope::Input => (kAudioUnitScope_Input, AU_IN_BUS), Scope::Output => (kAudioUnitScope_Output, AU_OUT_BUS), }; + let mut size = mem::size_of::(); assert_eq!( audio_unit_get_property( unit, @@ -510,7 +511,7 @@ pub fn test_audiounit_scope_is_enabled(unit: AudioUnit, scope: Scope) -> bool { scope, element, &mut has_io, - &mut mem::size_of::() + &mut size ), NO_ERR ); diff --git a/third_party/rust/cubeb-coreaudio/src/backend/utils.rs b/third_party/rust/cubeb-coreaudio/src/backend/utils.rs index fa760487713c..d76242729b17 100644 --- a/third_party/rust/cubeb-coreaudio/src/backend/utils.rs +++ b/third_party/rust/cubeb-coreaudio/src/backend/utils.rs @@ -5,18 +5,14 @@ use cubeb_backend::SampleFormat as fmt; use std::mem; -pub fn allocate_array_by_size(size: usize) -> Vec { +pub fn allocate_array_by_size(size: usize) -> Vec { assert_eq!(size % mem::size_of::(), 0); let elements = size / mem::size_of::(); allocate_array::(elements) } -pub fn allocate_array(elements: usize) -> Vec { - let mut array = Vec::::with_capacity(elements); - unsafe { - array.set_len(elements); - } - array +pub fn allocate_array(elements: usize) -> Vec { + vec![T::default(); elements] } pub fn forget_vec(v: Vec) -> (*mut T, usize) { @@ -43,7 +39,9 @@ struct Finalizer(Option); impl Drop for Finalizer { fn drop(&mut self) { - self.0.take().map(|f| f()); + if let Some(f) = self.0.take() { + f() + } } } diff --git a/third_party/rust/cubeb-coreaudio/src/capi.rs b/third_party/rust/cubeb-coreaudio/src/capi.rs index 3055ff4efc2a..1fd9e96f105d 100644 --- a/third_party/rust/cubeb-coreaudio/src/capi.rs +++ b/third_party/rust/cubeb-coreaudio/src/capi.rs @@ -7,7 +7,9 @@ use crate::backend::AudioUnitContext; use cubeb_backend::{capi, ffi}; use std::os::raw::{c_char, c_int}; -// Entry point from C code. +/// # Safety +/// +/// This function should only be called once per process. #[no_mangle] pub unsafe extern "C" fn audiounit_rust_init( c: *mut *mut ffi::cubeb, diff --git a/third_party/rust/cubeb-coreaudio/src/lib.rs b/third_party/rust/cubeb-coreaudio/src/lib.rs index 289ca21c5c73..87efbf54d6e3 100644 --- a/third_party/rust/cubeb-coreaudio/src/lib.rs +++ b/third_party/rust/cubeb-coreaudio/src/lib.rs @@ -9,6 +9,8 @@ extern crate bitflags; #[macro_use] extern crate cubeb_backend; #[macro_use] +extern crate float_cmp; +#[macro_use] extern crate lazy_static; extern crate mach; diff --git a/third_party/rust/float-cmp/.cargo-checksum.json b/third_party/rust/float-cmp/.cargo-checksum.json new file mode 100644 index 000000000000..64b9f0330375 --- /dev/null +++ b/third_party/rust/float-cmp/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"9dbf5b8451e618eaccd9c9e9360042f8e9aa1db0f05df9300e2b677a9393eff4","LICENSE":"0b4228e50c57acadaef1e9c39eb4494abe91b312088a8d1744ddf1a50310a915","README.md":"3dc6976c44ce23c76f38a9834b0cd3c4bd1d2103a7c3bcf71517b9722c07aae7","src/eq.rs":"44197a5635757405c59b3870fb4741bd2425a16be9a92657bd24adbb0ac7f7df","src/lib.rs":"ef87d7668ffa8bd4f80654871e1108b2e75b176fcd97b66dca0ef6924595cbe4","src/macros.rs":"85f7d267aee869b5a3c97727f6a6b66848ea3adcf7ea1f4ea0e3626264b216d0","src/ratio.rs":"4c097204d91a20346fcf1e44b94cb116bb6ee130517d29635ade58ddea08750e","src/ulps.rs":"bfea6ab54c98c8337c76c9efd50cbd4ba911ebc146c9b6cfa2952fc55740f01b","src/ulps_eq.rs":"28ff2086c0c11ddfbb40c5b4d5aa3f0c3f4d399137ee37c71a24e8e106b866d4"},"package":"da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"} \ No newline at end of file diff --git a/third_party/rust/float-cmp/Cargo.toml b/third_party/rust/float-cmp/Cargo.toml new file mode 100644 index 000000000000..e3b944960367 --- /dev/null +++ b/third_party/rust/float-cmp/Cargo.toml @@ -0,0 +1,37 @@ +# 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 = "float-cmp" +version = "0.6.0" +authors = ["Mike Dilger "] +description = "Floating point approximate comparison traits" +documentation = "https://docs.rs/float-cmp" +readme = "README.md" +keywords = ["float", "comparison", "fuzzy", "approximate"] +license = "MIT" +repository = "https://github.com/mikedilger/float-cmp" + +[lib] +name = "float_cmp" +path = "src/lib.rs" +test = true +doctest = true +doc = true +[dependencies.num-traits] +version = "0.2" +optional = true +default-features = false + +[features] +default = ["num-traits"] diff --git a/third_party/rust/float-cmp/LICENSE b/third_party/rust/float-cmp/LICENSE new file mode 100644 index 000000000000..75ad606734b1 --- /dev/null +++ b/third_party/rust/float-cmp/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2018 Optimal Computing (NZ) Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/third_party/rust/float-cmp/README.md b/third_party/rust/float-cmp/README.md new file mode 100644 index 000000000000..79949804ea29 --- /dev/null +++ b/third_party/rust/float-cmp/README.md @@ -0,0 +1,143 @@ +# float-cmp + +[![Build Status](https://travis-ci.org/mikedilger/float-cmp.svg?branch=master)](https://travis-ci.org/mikedilger/float-cmp) +[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) + +Documentation is available at https://docs.rs/float-cmp + +float-cmp defines and implements traits for approximate comparison of floating point types +which have fallen away from exact equality due to the limited precision available within +floating point representations. Implementations of these traits are provided for `f32` +and `f64` types. + +When I was a kid in the '80s, the programming rule was "Never compare floating point +numbers". If you can follow that rule and still get the outcome you desire, then more +power to you. However, if you really do need to compare them, this crate provides a +reasonable way to do so. + +Another crate `efloat` offers another solution by providing a floating point type that +tracks its error bounds as operations are performed on it, and thus can implement the +`ApproxEq` trait in this crate more accurately, without specifying a `Margin`. + +The recommended go-to solution (although it may not be appropriate in all cases) is the +`approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32` +and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as +both an epsilon value and an ULPs value, and defaults are provided via `Default` +(although there is no perfect default value that is always appropriate, so beware). + +Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and +`ApproxEqRatio`. + +## The problem + +Floating point operations must round answers to the nearest representable number. Multiple +operations may result in an answer different from what you expect. In the following example, +the assert will fail, even though the printed output says "0.45 == 0.45": + +```rust + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + println!("{} == {}", a, b); + assert!(a==b) // Fails, because they are not exactly equal +``` + +This fails because the correct answer to most operations isn't exactly representable, and so +your computer's processor chooses to represent the answer with the closest value it has +available. This introduces error, and this error can accumulate as multiple operations are +performed. + +## The solution + +With `ApproxEq`, we can get the answer we intend: +```rust + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + println!("{} == {}", a, b); + assert!( approx_eq!(f32, a, b, ulps = 2) ); +``` + +## Some explanation + +We use the term ULP (units of least precision, or units in the last place) to mean the +difference between two adjacent floating point representations (adjacent meaning that there is +no floating point number between them). This term is borrowed from prior work (personally I +would have chosen "quanta"). The size of an ULP (measured as a float) varies +depending on the exponents of the floating point numbers in question. That is a good thing, +because as numbers fall away from equality due to the imprecise nature of their representation, +they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are +absolute and thus don't map well to the nature of the additive error issue. They work fine +for many ranges of numbers, but not for others (consider comparing -0.0000000028 +to +0.00000097). + +## Using this crate + +You can use the `ApproxEq` trait directly like so: + +```rust + assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) ); +``` + +We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`) +so you can use this shorthand: + +```rust + assert!( a.approx_eq(b, (0.0, 2)) ); +``` + +With macros, it is easier to be explicit about which type of margin you wish to set, +without mentioning the other one (the other one will be zero). But the downside is +that you have to specify the type you are dealing with: + +```rust + assert!( approx_eq!(f32, a, b, ulps = 2) ); + assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) ); + assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) ); + assert!( approx_eq!(f32, a, b, (0.0, 2)) ); + assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) ); + assert!( approx_eq!(f32, a, b, F32Margin::default()) ); + assert!( approx_eq!(f32, a, b) ); // uses the default +``` + +For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5 +or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0 +or so), but there are *plenty* of cases where this is insufficient. + +## Implementing these traits + +You can implement `ApproxEq` for your own complex types like shown below. +The floating point type `F` must be `Copy`, but for large types you can implement +it for references to your type as shown. + +```rust +use float_cmp::ApproxEq; + +pub struct Vec2 { + pub x: F, + pub y: F, +} + +impl<'a, M: Copy, F: Copy + ApproxEq> ApproxEq for &'a Vec2 { + type Margin = M; + + fn approx_eq>(self, other: Self, margin: T) -> bool { + let margin = margin.into(); + self.x.approx_eq(other.x, margin) + && self.y.approx_eq(other.y, margin) + } +} +``` + +## Non floating-point types + +`ApproxEq` can be implemented for non floating-point types as well, since `Margin` is +an associated type. + +The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type +that tracks floating point error bounds by checking if the error bounds overlap. +In that case `type Margin = ()`. + +## Inspiration + +This crate was inspired by this Random ASCII blog post: + +[https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) diff --git a/third_party/rust/float-cmp/src/eq.rs b/third_party/rust/float-cmp/src/eq.rs new file mode 100644 index 000000000000..d39535c95a15 --- /dev/null +++ b/third_party/rust/float-cmp/src/eq.rs @@ -0,0 +1,284 @@ +// Copyright 2014-2018 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +use std::{f32,f64}; +use super::Ulps; + +/// ApproxEq is a trait for approximate equality comparisons. +pub trait ApproxEq: Sized { + /// The Margin type defines a margin within which two values are to be + /// considered approximately equal. It must implement Default so that + /// approx_eq() can be called on unknown types. + type Margin: Copy + Default; + + /// This method tests for `self` and `other` values to be approximately equal + /// within `margin`. + fn approx_eq>(self, other: Self, margin: M) -> bool; + + /// This method tests for `self` and `other` values to be not approximately + /// equal within `margin`. + fn approx_ne>(self, other: Self, margin: M) -> bool { + !self.approx_eq(other, margin) + } +} + +/// This type defines a margin within two f32s might be considered equal +/// and is intended as the associated type for the `ApproxEq` trait. +/// +/// Two methods are used to determine approximate equality. +/// +/// First an epsilon method is used, considering them approximately equal if they +/// differ by <= `epsilon`. This will only succeed for very small numbers. +/// Note that it may succeed even if the parameters are of differing signs straddling +/// zero. +/// +/// The second method considers how many ULPs (units of least precision, units in +/// the last place, which is the integer number of floating point representations +/// that the parameters are separated by) different the parameters are and considers +/// them approximately equal if this is <= `ulps`. For large floating point numbers, +/// an ULP can be a rather large gap, but this kind of comparison is necessary +/// because floating point operations must round to the nearest representable value +/// and so larger floating point values accumulate larger errors. +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct F32Margin { + pub epsilon: f32, + pub ulps: i32 +} +impl Default for F32Margin { + #[inline] + fn default() -> F32Margin { + F32Margin { + epsilon: f32::EPSILON, + ulps: 4 + } + } +} +impl F32Margin { + #[inline] + pub fn zero() -> F32Margin { + F32Margin { + epsilon: 0.0, + ulps: 0 + } + } + pub fn epsilon(self, epsilon: f32) -> Self { + F32Margin { + epsilon: epsilon, + ..self + } + } + pub fn ulps(self, ulps: i32) -> Self { + F32Margin { + ulps: ulps, + ..self + } + } +} +impl From<(f32, i32)> for F32Margin { + fn from(m: (f32, i32)) -> F32Margin { + F32Margin { + epsilon: m.0, + ulps: m.1 + } + } +} + +impl ApproxEq for f32 { + type Margin = F32Margin; + + fn approx_eq>(self, other: f32, margin: M) -> bool { + let margin = margin.into(); + + // Check for exact equality first. This is often true, and so we get the + // performance benefit of only doing one compare in most cases. + self==other || + + // Perform epsilon comparison next + ((self - other).abs() <= margin.epsilon) || + + { + // Perform ulps comparion last + let diff: i32 = self.ulps(&other); + saturating_abs_i32!(diff) <= margin.ulps + } + } +} + +#[test] +fn f32_approx_eq_test1() { + let f: f32 = 0.0_f32; + let g: f32 = -0.0000000000000005551115123125783_f32; + assert!(f != g); // Should not be directly equal + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true); +} +#[test] +fn f32_approx_eq_test2() { + let f: f32 = 0.0_f32; + let g: f32 = -0.0_f32; + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true); +} +#[test] +fn f32_approx_eq_test3() { + let f: f32 = 0.0_f32; + let g: f32 = 0.00000000000000001_f32; + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true); +} +#[test] +fn f32_approx_eq_test4() { + let f: f32 = 0.00001_f32; + let g: f32 = 0.00000000000000001_f32; + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == false); +} +#[test] +fn f32_approx_eq_test5() { + let f: f32 = 0.1_f32; + let mut sum: f32 = 0.0_f32; + for _ in 0_isize..10_isize { sum += f; } + let product: f32 = f * 10.0_f32; + assert!(sum != product); // Should not be directly equal: + println!("Ulps Difference: {}",sum.ulps(&product)); + assert!(sum.approx_eq(product, (f32::EPSILON, 1)) == true); + assert!(sum.approx_eq(product, F32Margin::zero()) == false); +} +#[test] +fn f32_approx_eq_test6() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + assert!(x != y); // Should not be directly equal + assert!(x.approx_eq(y, (0.0, 2)) == true); // 2 ulps does it + // epsilon method no good here: + assert!(x.approx_eq(y, (1000.0 * f32::EPSILON, 0)) == false); +} + +/// This type defines a margin within two f32s might be considered equal +/// and is intended as the associated type for the `ApproxEq` trait. +/// +/// Two methods are used to determine approximate equality. +/// +/// First an epsilon method is used, considering them approximately equal if they +/// differ by <= `epsilon`. This will only succeed for very small numbers. +/// Note that it may succeed even if the parameters are of differing signs straddling +/// zero. +/// +/// The second method considers how many ULPs (units of least precision, units in +/// the last place, which is the integer number of floating point representations +/// that the parameters are separated by) different the parameters are and considers +/// them approximately equal if this <= `ulps`. For large floating point numbers, +/// an ULP can be a rather large gap, but this kind of comparison is necessary +/// because floating point operations must round to the nearest representable value +/// and so larger floating point values accumulate larger errors. +#[derive(Debug, Clone, Copy)] +pub struct F64Margin { + pub epsilon: f64, + pub ulps: i64 +} +impl Default for F64Margin { + #[inline] + fn default() -> F64Margin { + F64Margin { + epsilon: f64::EPSILON, + ulps: 4 + } + } +} +impl F64Margin { + #[inline] + pub fn zero() -> F64Margin { + F64Margin { + epsilon: 0.0, + ulps: 0 + } + } + pub fn epsilon(self, epsilon: f64) -> Self { + F64Margin { + epsilon: epsilon, + ..self + } + } + pub fn ulps(self, ulps: i64) -> Self { + F64Margin { + ulps: ulps, + ..self + } + } +} +impl From<(f64, i64)> for F64Margin { + fn from(m: (f64, i64)) -> F64Margin { + F64Margin { + epsilon: m.0, + ulps: m.1 + } + } +} + +impl ApproxEq for f64 { + type Margin = F64Margin; + + fn approx_eq>(self, other: f64, margin: M) -> bool { + let margin = margin.into(); + + // Check for exact equality first. This is often true, and so we get the + // performance benefit of only doing one compare in most cases. + self==other || + + // Perform epsilon comparison next + ((self - other).abs() <= margin.epsilon) || + + { + // Perform ulps comparion last + let diff: i64 = self.ulps(&other); + saturating_abs_i64!(diff) <= margin.ulps + } + } +} + +#[test] +fn f64_approx_eq_test1() { + let f: f64 = 0.0_f64; + let g: f64 = -0.0000000000000005551115123125783_f64; + assert!(f != g); // Should not be directly equal + assert!(f.approx_eq(g, (3.0 * f64::EPSILON, 0)) == true); // 3e is enough + // ulps test wont ever call these equal +} +#[test] +fn f64_approx_eq_test2() { + let f: f64 = 0.0_f64; + let g: f64 = -0.0_f64; + assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true); +} +#[test] +fn f64_approx_eq_test3() { + let f: f64 = 0.0_f64; + let g: f64 = 1e-17_f64; + assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true); +} +#[test] +fn f64_approx_eq_test4() { + let f: f64 = 0.00001_f64; + let g: f64 = 0.00000000000000001_f64; + assert!(f.approx_eq(g, (f64::EPSILON, 0)) == false); +} +#[test] +fn f64_approx_eq_test5() { + let f: f64 = 0.1_f64; + let mut sum: f64 = 0.0_f64; + for _ in 0_isize..10_isize { sum += f; } + let product: f64 = f * 10.0_f64; + assert!(sum != product); // Should not be directly equal: + println!("Ulps Difference: {}",sum.ulps(&product)); + assert!(sum.approx_eq(product, (f64::EPSILON, 0)) == true); + assert!(sum.approx_eq(product, (0.0, 1)) == true); +} +#[test] +fn f64_approx_eq_test6() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.0000000003_f64; + assert!(x != y); // Should not be directly equal + println!("Ulps Difference: {}",x.ulps(&y)); + assert!(x.approx_eq(y, (0.0, 3)) == true); +} +#[test] +fn f64_code_triggering_issue_20() { + assert_eq!((-25.0f64).approx_eq(25.0, (0.00390625, 1)), false); +} diff --git a/third_party/rust/float-cmp/src/lib.rs b/third_party/rust/float-cmp/src/lib.rs new file mode 100644 index 000000000000..16ad9d1cef52 --- /dev/null +++ b/third_party/rust/float-cmp/src/lib.rs @@ -0,0 +1,196 @@ +// Copyright 2014-2018 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +//! # float-cmp +//! +//! float-cmp defines and implements traits for approximate comparison of floating point types +//! which have fallen away from exact equality due to the limited precision available within +//! floating point representations. Implementations of these traits are provided for `f32` +//! and `f64` types. +//! +//! When I was a kid in the '80s, the programming rule was "Never compare floating point +//! numbers". If you can follow that rule and still get the outcome you desire, then more +//! power to you. However, if you really do need to compare them, this crate provides a +//! reasonable way to do so. +//! +//! Another crate `efloat` offers another solution by providing a floating point type that +//! tracks its error bounds as operations are performed on it, and thus can implement the +//! `ApproxEq` trait in this crate more accurately, without specifying a `Margin`. +//! +//! The recommended go-to solution (although it may not be appropriate in all cases) is the +//! `approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32` +//! and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as +//! both an epsilon value and an ULPs value, and defaults are provided via `Default` +//! (although there is no perfect default value that is always appropriate, so beware). +//! +//! Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and +//! `ApproxEqRatio`. +//! +//! ## The problem +//! +//! Floating point operations must round answers to the nearest representable number. Multiple +//! operations may result in an answer different from what you expect. In the following example, +//! the assert will fail, even though the printed output says "0.45 == 0.45": +//! +//! ```should_panic +//! # extern crate float_cmp; +//! # use float_cmp::ApproxEq; +//! # fn main() { +//! let a: f32 = 0.15 + 0.15 + 0.15; +//! let b: f32 = 0.1 + 0.1 + 0.25; +//! println!("{} == {}", a, b); +//! assert!(a==b) // Fails, because they are not exactly equal +//! # } +//! ``` +//! +//! This fails because the correct answer to most operations isn't exactly representable, and so +//! your computer's processor chooses to represent the answer with the closest value it has +//! available. This introduces error, and this error can accumulate as multiple operations are +//! performed. +//! +//! ## The solution +//! +//! With `ApproxEq`, we can get the answer we intend: +//! +//! ``` +//! # #[macro_use] +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! let a: f32 = 0.15 + 0.15 + 0.15; +//! let b: f32 = 0.1 + 0.1 + 0.25; +//! println!("{} == {}", a, b); +//! // They are equal, within 2 ulps +//! assert!( approx_eq!(f32, a, b, ulps = 2) ); +//! # } +//! ``` +//! +//! ## Some explanation +//! +//! We use the term ULP (units of least precision, or units in the last place) to mean the +//! difference between two adjacent floating point representations (adjacent meaning that there is +//! no floating point number between them). This term is borrowed from prior work (personally I +//! would have chosen "quanta"). The size of an ULP (measured as a float) varies +//! depending on the exponents of the floating point numbers in question. That is a good thing, +//! because as numbers fall away from equality due to the imprecise nature of their representation, +//! they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are +//! absolute and thus don't map well to the nature of the additive error issue. They work fine +//! for many ranges of numbers, but not for others (consider comparing -0.0000000028 +//! to +0.00000097). +//! +//! ## Using this crate +//! +//! You can use the `ApproxEq` trait directly like so: +//! +//! ``` +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! # let a: f32 = 0.15 + 0.15 + 0.15; +//! # let b: f32 = 0.1 + 0.1 + 0.25; +//! assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) ); +//! # } +//! ``` +//! +//! We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`) +//! so you can use this shorthand: +//! +//! ``` +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! # let a: f32 = 0.15 + 0.15 + 0.15; +//! # let b: f32 = 0.1 + 0.1 + 0.25; +//! assert!( a.approx_eq(b, (0.0, 2)) ); +//! # } +//! ``` +//! +//! With macros, it is easier to be explicit about which type of margin you wish to set, +//! without mentioning the other one (the other one will be zero). But the downside is +//! that you have to specify the type you are dealing with: +//! +//! ``` +//! # #[macro_use] +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! # let a: f32 = 0.15 + 0.15 + 0.15; +//! # let b: f32 = 0.1 + 0.1 + 0.25; +//! assert!( approx_eq!(f32, a, b, ulps = 2) ); +//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) ); +//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) ); +//! assert!( approx_eq!(f32, a, b, (0.0, 2)) ); +//! assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) ); +//! assert!( approx_eq!(f32, a, b, F32Margin::default()) ); +//! assert!( approx_eq!(f32, a, b) ); // uses the default +//! # } +//! ``` +//! +//! For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5 +//! or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0 +//! or so), but there are *plenty* of cases where this is insufficient. +//! +//! ## Implementing these traits +//! +//! You can implement `ApproxEq` for your own complex types like shown below. +//! The floating point type `F` must be `Copy`, but for large types you can implement +//! it for references to your type as shown. +//! +//! ``` +//! use float_cmp::ApproxEq; +//! +//! pub struct Vec2 { +//! pub x: F, +//! pub y: F, +//! } +//! +//! impl<'a, M: Copy + Default, F: Copy + ApproxEq> ApproxEq for &'a Vec2 { +//! type Margin = M; +//! +//! fn approx_eq>(self, other: Self, margin: T) -> bool { +//! let margin = margin.into(); +//! self.x.approx_eq(other.x, margin) +//! && self.y.approx_eq(other.y, margin) +//! } +//! } +//! ``` +//! +//! ## Non floating-point types +//! +//! `ApproxEq` can be implemented for non floating-point types as well, since `Margin` is +//! an associated type. +//! +//! The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type +//! that tracks floating point error bounds by checking if the error bounds overlap. +//! In that case `type Margin = ()`. +//! +//! ## Inspiration +//! +//! This crate was inspired by this Random ASCII blog post: +//! +//! [https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) + +#[cfg(feature="num-traits")] +extern crate num_traits; + +#[macro_use] +mod macros; + +pub fn trials() { + println!("are they approximately equal?: {:?}", + approx_eq!(f32, 1.0, 1.0000001)); +} + +mod ulps; +pub use self::ulps::Ulps; + +mod ulps_eq; +pub use self::ulps_eq::ApproxEqUlps; + +mod eq; +pub use self::eq::{ApproxEq, F32Margin, F64Margin}; + +#[cfg(feature="num-traits")] +mod ratio; +#[cfg(feature="num-traits")] +pub use self::ratio::ApproxEqRatio; diff --git a/third_party/rust/float-cmp/src/macros.rs b/third_party/rust/float-cmp/src/macros.rs new file mode 100644 index 000000000000..7eeaa30fd9eb --- /dev/null +++ b/third_party/rust/float-cmp/src/macros.rs @@ -0,0 +1,77 @@ + +#[macro_export] +macro_rules! approx_eq { + ($typ:ty, $lhs:expr, $rhs:expr) => { + { + let m: <$typ as $crate::ApproxEq>::Margin = Default::default(); + <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m) + } + }; + ($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => { + { + let m = <$typ as $crate::ApproxEq>::Margin::zero()$(.$set($val))*; + <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m) + } + }; + ($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => { + { + <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, $marg) + } + }; +} + +// Until saturating_abs() comes out of nightly, we have to code it ourselves. +macro_rules! saturating_abs_i32 { + ($val:expr) => { + if $val.is_negative() { + match $val.checked_neg() { + Some(v) => v, + None => std::i32::MAX + } + } else { + $val + } + }; +} +macro_rules! saturating_abs_i64 { + ($val:expr) => { + if $val.is_negative() { + match $val.checked_neg() { + Some(v) => v, + None => std::i64::MAX + } + } else { + $val + } + }; +} + +#[test] +fn test_macro() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + assert!( approx_eq!(f32, a, b) ); // uses the default + assert!( approx_eq!(f32, a, b, ulps = 2) ); + assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) ); + assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) ); + assert!( approx_eq!(f32, a, b, (0.0, 2)) ); +} + +#[test] +fn test_macro_2() { + assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64) ); + assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps=3) ); + assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, epsilon=0.0000000004) ); + assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0)) ); + assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3)) ); +} + +#[test] +fn test_macro_3() { + use crate::F32Margin; + + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) ); + assert!( approx_eq!(f32, a, b, F32Margin::default()) ); +} diff --git a/third_party/rust/float-cmp/src/ratio.rs b/third_party/rust/float-cmp/src/ratio.rs new file mode 100644 index 000000000000..0a8654bba891 --- /dev/null +++ b/third_party/rust/float-cmp/src/ratio.rs @@ -0,0 +1,142 @@ +// Copyright 2014-2018 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +use std::cmp::PartialOrd; +use std::ops::{Sub,Div,Neg}; +use num_traits::Zero; + +/// ApproxEqRatio is a trait for approximate equality comparisons bounding the ratio +/// of the difference to the larger. +pub trait ApproxEqRatio : Div + Sub + Neg + + PartialOrd + Zero + Sized + Copy +{ + /// This method tests if `self` and `other` are nearly equal by bounding the + /// difference between them to some number much less than the larger of the two. + /// This bound is set as the ratio of the difference to the larger. + fn approx_eq_ratio(&self, other: &Self, ratio: Self) -> bool { + + // Not equal if signs are not equal + if *self < Self::zero() && *other > Self::zero() { return false; } + if *self > Self::zero() && *other < Self::zero() { return false; } + + // Handle all zero cases + match (*self == Self::zero(), *other == Self::zero()) { + (true,true) => return true, + (true,false) => return false, + (false,true) => return false, + _ => { } + } + + // abs + let (s,o) = if *self < Self::zero() { + (-*self, -*other) + } else { + (*self, *other) + }; + + let (smaller,larger) = if s < o { + (s,o) + } else { + (o,s) + }; + let difference: Self = larger.sub(smaller); + let actual_ratio: Self = difference.div(larger); + actual_ratio < ratio + } + + /// This method tests if `self` and `other` are not nearly equal by bounding the + /// difference between them to some number much less than the larger of the two. + /// This bound is set as the ratio of the difference to the larger. + #[inline] + fn approx_ne_ratio(&self, other: &Self, ratio: Self) -> bool { + !self.approx_eq_ratio(other, ratio) + } +} + +impl ApproxEqRatio for f32 { } + +#[test] +fn f32_approx_eq_ratio_test1() { + let x: f32 = 0.00004_f32; + let y: f32 = 0.00004001_f32; + assert!(x.approx_eq_ratio(&y, 0.00025)); + assert!(y.approx_eq_ratio(&x, 0.00025)); + assert!(x.approx_ne_ratio(&y, 0.00024)); + assert!(y.approx_ne_ratio(&x, 0.00024)); +} + +#[test] +fn f32_approx_eq_ratio_test2() { + let x: f32 = 0.00000000001_f32; + let y: f32 = 0.00000000005_f32; + assert!(x.approx_eq_ratio(&y, 0.81)); + assert!(y.approx_ne_ratio(&x, 0.79)); +} + +#[test] +fn f32_approx_eq_ratio_test_zero_eq_zero_returns_true() { + let x: f32 = 0.0_f32; + assert!(x.approx_eq_ratio(&x,0.1) == true); +} + +#[test] +fn f32_approx_eq_ratio_test_zero_ne_zero_returns_false() { + let x: f32 = 0.0_f32; + assert!(x.approx_ne_ratio(&x,0.1) == false); +} + +#[test] +fn f32_approx_eq_ratio_test_against_a_zero_is_false() { + let x: f32 = 0.0_f32; + let y: f32 = 0.1_f32; + assert!(x.approx_eq_ratio(&y,0.1) == false); + assert!(y.approx_eq_ratio(&x,0.1) == false); +} + +#[test] +fn f32_approx_eq_ratio_test_negative_numbers() { + let x: f32 = -3.0_f32; + let y: f32 = -4.0_f32; + // -3 and -4 should not be equal at a ratio of 0.1 + assert!(x.approx_eq_ratio(&y,0.1) == false); +} + +impl ApproxEqRatio for f64 { } + +#[test] +fn f64_approx_eq_ratio_test1() { + let x: f64 = 0.000000004_f64; + let y: f64 = 0.000000004001_f64; + assert!(x.approx_eq_ratio(&y, 0.00025)); + assert!(y.approx_eq_ratio(&x, 0.00025)); + assert!(x.approx_ne_ratio(&y, 0.00024)); + assert!(y.approx_ne_ratio(&x, 0.00024)); +} + +#[test] +fn f64_approx_eq_ratio_test2() { + let x: f64 = 0.0000000000000001_f64; + let y: f64 = 0.0000000000000005_f64; + assert!(x.approx_eq_ratio(&y, 0.81)); + assert!(y.approx_ne_ratio(&x, 0.79)); +} + +#[test] +fn f64_approx_eq_ratio_test_zero_eq_zero_returns_true() { + let x: f64 = 0.0_f64; + assert!(x.approx_eq_ratio(&x,0.1) == true); +} + +#[test] +fn f64_approx_eq_ratio_test_zero_ne_zero_returns_false() { + let x: f64 = 0.0_f64; + assert!(x.approx_ne_ratio(&x,0.1) == false); +} + +#[test] +fn f64_approx_eq_ratio_test_negative_numbers() { + let x: f64 = -3.0_f64; + let y: f64 = -4.0_f64; + // -3 and -4 should not be equal at a ratio of 0.1 + assert!(x.approx_eq_ratio(&y,0.1) == false); +} diff --git a/third_party/rust/float-cmp/src/ulps.rs b/third_party/rust/float-cmp/src/ulps.rs new file mode 100644 index 000000000000..51f186f94efc --- /dev/null +++ b/third_party/rust/float-cmp/src/ulps.rs @@ -0,0 +1,240 @@ +// Copyright 2014-2018 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +#[cfg(feature="num_traits")] +use num_traits::NumCast; + +/// A trait for floating point numbers which computes the number of representable +/// values or ULPs (Units of Least Precision) that separate the two given values. +#[cfg(feature="num_traits")] +pub trait Ulps { + type U: Copy + NumCast; + + /// The number of representable values or ULPs (Units of Least Precision) that + /// separate `self` and `other`. The result `U` is an integral value, and will + /// be zero if `self` and `other` are exactly equal. + fn ulps(&self, other: &Self) -> ::U; + + /// The next representable number above this one + fn next(&self) -> Self; + + /// The previous representable number below this one + fn prev(&self) -> Self; +} + +#[cfg(not(feature="num_traits"))] +pub trait Ulps { + type U: Copy; + + /// The number of representable values or ULPs (Units of Least Precision) that + /// separate `self` and `other`. The result `U` is an integral value, and will + /// be zero if `self` and `other` are exactly equal. + fn ulps(&self, other: &Self) -> ::U; + + /// The next representable number above this one + fn next(&self) -> Self; + + /// The previous representable number below this one + fn prev(&self) -> Self; +} + +impl Ulps for f32 { + type U = i32; + + fn ulps(&self, other: &f32) -> i32 { + + // IEEE754 defined floating point storage representation to + // maintain their order when their bit patterns are interpreted as + // integers. This is a huge boon to the task at hand, as we can + // reinterpret them as integers to find out how many ULPs apart any + // two floats are + + // Setup integer representations of the input + let ai32: i32 = self.to_bits() as i32; + let bi32: i32 = other.to_bits() as i32; + + ai32.wrapping_sub(bi32) + } + + fn next(&self) -> Self { + if self.is_infinite() && *self > 0.0 { + *self + } else if *self == -0.0 && self.is_sign_negative() { + 0.0 + } else { + let mut u = self.to_bits(); + if *self >= 0.0 { + u += 1; + } else { + u -= 1; + } + f32::from_bits(u) + } + } + + fn prev(&self) -> Self { + if self.is_infinite() && *self < 0.0 { + *self + } else if *self == 0.0 && self.is_sign_positive() { + -0.0 + } else { + let mut u = self.to_bits(); + if *self <= -0.0 { + u += 1; + } else { + u -= 1; + } + f32::from_bits(u) + } + } +} + +#[test] +fn f32_ulps_test1() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == -2); +} + +#[test] +fn f32_ulps_test2() { + let pzero: f32 = f32::from_bits(0x00000000_u32); + let nzero: f32 = f32::from_bits(0x80000000_u32); + println!("DIST IS {}",pzero.ulps(&nzero)); + assert!(pzero.ulps(&nzero) == -2147483648); +} +#[test] +fn f32_ulps_test3() { + let pinf: f32 = f32::from_bits(0x7f800000_u32); + let ninf: f32 = f32::from_bits(0xff800000_u32); + println!("DIST IS {}",pinf.ulps(&ninf)); + assert!(pinf.ulps(&ninf) == -2147483648); +} + +#[test] +fn f32_ulps_test4() { + let x: f32 = f32::from_bits(0x63a7f026_u32); + let y: f32 = f32::from_bits(0x63a7f023_u32); + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == 3); +} + +#[test] +fn f32_ulps_test5() { + let x: f32 = 2.0; + let ulps: i32 = x.to_bits() as i32; + let x2: f32 = ::from_bits(ulps as u32); + assert_eq!(x, x2); +} + +#[test] +fn f32_ulps_test6() { + let negzero: f32 = -0.; + let zero: f32 = 0.; + assert_eq!(negzero.next(), zero); + assert_eq!(zero.prev(), negzero); + assert!(negzero.prev() < 0.0); + assert!(zero.next() > 0.0); +} + +impl Ulps for f64 { + type U = i64; + + fn ulps(&self, other: &f64) -> i64 { + + // IEEE754 defined floating point storage representation to + // maintain their order when their bit patterns are interpreted as + // integers. This is a huge boon to the task at hand, as we can + // reinterpret them as integers to find out how many ULPs apart any + // two floats are + + // Setup integer representations of the input + let ai64: i64 = self.to_bits() as i64; + let bi64: i64 = other.to_bits() as i64; + + ai64.wrapping_sub(bi64) + } + + fn next(&self) -> Self { + if self.is_infinite() && *self > 0.0 { + *self + } else if *self == -0.0 && self.is_sign_negative() { + 0.0 + } else { + let mut u = self.to_bits(); + if *self >= 0.0 { + u += 1; + } else { + u -= 1; + } + f64::from_bits(u) + } + } + + fn prev(&self) -> Self { + if self.is_infinite() && *self < 0.0 { + *self + } else if *self == 0.0 && self.is_sign_positive() { + -0.0 + } else { + let mut u = self.to_bits(); + if *self <= -0.0 { + u += 1; + } else { + u -= 1; + } + f64::from_bits(u) + } + } +} + +#[test] +fn f64_ulps_test1() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.00000001_f64; + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == -86); +} + +#[test] +fn f64_ulps_test2() { + let pzero: f64 = f64::from_bits(0x0000000000000000_u64); + let nzero: f64 = f64::from_bits(0x8000000000000000_u64); + println!("DIST IS {}",pzero.ulps(&nzero)); + assert!(pzero.ulps(&nzero) == -9223372036854775808i64); +} +#[test] +fn f64_ulps_test3() { + let pinf: f64 = f64::from_bits(0x7f80000000000000_u64); + let ninf: f64 = f64::from_bits(0xff80000000000000_u64); + println!("DIST IS {}",pinf.ulps(&ninf)); + assert!(pinf.ulps(&ninf) == -9223372036854775808i64); +} + +#[test] +fn f64_ulps_test4() { + let x: f64 = f64::from_bits(0xd017f6cc63a7f026_u64); + let y: f64 = f64::from_bits(0xd017f6cc63a7f023_u64); + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == 3); +} + +#[test] +fn f64_ulps_test5() { + let x: f64 = 2.0; + let ulps: i64 = x.to_bits() as i64; + let x2: f64 = ::from_bits(ulps as u64); + assert_eq!(x, x2); +} + +#[test] +fn f64_ulps_test6() { + let negzero: f64 = -0.; + let zero: f64 = 0.; + assert_eq!(negzero.next(), zero); + assert_eq!(zero.prev(), negzero); + assert!(negzero.prev() < 0.0); + assert!(zero.next() > 0.0); +} + diff --git a/third_party/rust/float-cmp/src/ulps_eq.rs b/third_party/rust/float-cmp/src/ulps_eq.rs new file mode 100644 index 000000000000..9dc0c37f404d --- /dev/null +++ b/third_party/rust/float-cmp/src/ulps_eq.rs @@ -0,0 +1,119 @@ +// Copyright 2014-2018 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +use super::Ulps; + +/// ApproxEqUlps is a trait for approximate equality comparisons. +/// The associated type Flt is a floating point type which implements Ulps, and is +/// required so that this trait can be implemented for compound types (e.g. vectors), +/// not just for the floats themselves. +pub trait ApproxEqUlps { + type Flt: Ulps; + + /// This method tests for `self` and `other` values to be approximately equal + /// within ULPs (Units of Least Precision) floating point representations. + /// Differing signs are always unequal with this method, and zeroes are only + /// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more + /// appropriate. + fn approx_eq_ulps(&self, other: &Self, ulps: ::U) -> bool; + + /// This method tests for `self` and `other` values to be not approximately + /// equal within ULPs (Units of Least Precision) floating point representations. + /// Differing signs are always unequal with this method, and zeroes are only + /// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more + /// appropriate. + #[inline] + fn approx_ne_ulps(&self, other: &Self, ulps: ::U) -> bool { + !self.approx_eq_ulps(other, ulps) + } +} + +impl ApproxEqUlps for f32 { + type Flt = f32; + + fn approx_eq_ulps(&self, other: &f32, ulps: i32) -> bool { + // -0 and +0 are drastically far in ulps terms, so + // we need a special case for that. + if *self==*other { return true; } + + // Handle differing signs as a special case, even if + // they are very close, most people consider them + // unequal. + if self.is_sign_positive() != other.is_sign_positive() { return false; } + + let diff: i32 = self.ulps(other); + diff >= -ulps && diff <= ulps + } +} + +#[test] +fn f32_approx_eq_ulps_test1() { + let f: f32 = 0.1_f32; + let mut sum: f32 = 0.0_f32; + for _ in 0_isize..10_isize { sum += f; } + let product: f32 = f * 10.0_f32; + assert!(sum != product); // Should not be directly equal: + println!("Ulps Difference: {}",sum.ulps(&product)); + assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close + assert!(sum.approx_eq_ulps(&product,0) == false); +} +#[test] +fn f32_approx_eq_ulps_test2() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + assert!(x != y); // Should not be directly equal + println!("Ulps Difference: {}",x.ulps(&y)); + assert!(x.approx_eq_ulps(&y,2) == true); + assert!(x.approx_eq_ulps(&y,1) == false); +} +#[test] +fn f32_approx_eq_ulps_test_zeroes() { + let x: f32 = 0.0_f32; + let y: f32 = -0.0_f32; + assert!(x.approx_eq_ulps(&y,0) == true); +} + +impl ApproxEqUlps for f64 { + type Flt = f64; + + fn approx_eq_ulps(&self, other: &f64, ulps: i64) -> bool { + // -0 and +0 are drastically far in ulps terms, so + // we need a special case for that. + if *self==*other { return true; } + + // Handle differing signs as a special case, even if + // they are very close, most people consider them + // unequal. + if self.is_sign_positive() != other.is_sign_positive() { return false; } + + let diff: i64 = self.ulps(other); + diff >= -ulps && diff <= ulps + } +} + +#[test] +fn f64_approx_eq_ulps_test1() { + let f: f64 = 0.1_f64; + let mut sum: f64 = 0.0_f64; + for _ in 0_isize..10_isize { sum += f; } + let product: f64 = f * 10.0_f64; + assert!(sum != product); // Should not be directly equal: + println!("Ulps Difference: {}",sum.ulps(&product)); + assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close + assert!(sum.approx_eq_ulps(&product,0) == false); +} +#[test] +fn f64_approx_eq_ulps_test2() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.0000000003_f64; + assert!(x != y); // Should not be directly equal + println!("Ulps Difference: {}",x.ulps(&y)); + assert!(x.approx_eq_ulps(&y,3) == true); + assert!(x.approx_eq_ulps(&y,2) == false); +} +#[test] +fn f64_approx_eq_ulps_test_zeroes() { + let x: f64 = 0.0_f64; + let y: f64 = -0.0_f64; + assert!(x.approx_eq_ulps(&y,0) == true); +} diff --git a/toolkit/library/rust/shared/Cargo.toml b/toolkit/library/rust/shared/Cargo.toml index 086d3995097e..8f9cf1ba35a2 100644 --- a/toolkit/library/rust/shared/Cargo.toml +++ b/toolkit/library/rust/shared/Cargo.toml @@ -19,7 +19,7 @@ static_prefs = { path = "../../../../modules/libpref/init/static_prefs" } profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true } mozurl = { path = "../../../../netwerk/base/mozurl" } webrender_bindings = { path = "../../../../gfx/webrender_bindings", optional = true } -cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "7fe03b4201160f84aad1c12536affe6b05f8839f", optional = true } +cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "4acd80233efa645ac79769f37b07d495c1b42070", optional = true } cubeb-pulse = { git = "https://github.com/djg/cubeb-pulse-rs", rev="8069f8f4189982e0b38fa6dc8993dd4fab41f728", optional = true, features=["pulse-dlopen"] } cubeb-sys = { version = "0.6", optional = true, features=["gecko-in-tree"] } encoding_glue = { path = "../../../../intl/encoding_glue" }