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
This commit is contained in:
Chun-Min Chang 2020-02-26 15:25:36 +00:00
parent 9b7d17bb03
commit eb3b669f6b
31 changed files with 1454 additions and 66 deletions

View File

@ -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"

14
Cargo.lock generated
View File

@ -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"

View File

@ -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}
{"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}

View File

@ -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<T>(
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<T>(
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<T>(
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 {

View File

@ -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}
{"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}

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -83,7 +83,7 @@ pub fn get_device_label(
id: AudioDeviceID,
devtype: DeviceType,
) -> std::result::Result<StringRef, OSStatus> {
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,

View File

@ -44,13 +44,17 @@ pub fn get_channel_order(channel_layout: ChannelLayout) -> Vec<audio_mixer::Chan
fn get_default_channel_order(channel_count: usize) -> Vec<audio_mixer::Channel> {
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());
}
}
}

View File

@ -194,7 +194,7 @@ fn create_device_info(id: AudioDeviceID, devtype: DeviceType) -> Result<device_i
assert_ne!(id, kAudioObjectSystemObject);
let mut info = device_info {
id: id,
id,
flags: match devtype {
DeviceType::INPUT => device_flags::DEV_INPUT,
DeviceType::OUTPUT => device_flags::DEV_OUTPUT,
@ -328,9 +328,9 @@ fn get_volume(unit: AudioUnit) -> Result<f32> {
}
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<mixer::
kAudioUnitScope_Output,
AU_OUT_BUS,
&mut size,
ptr::null_mut(),
None,
);
if rv != NO_ERR {
cubeb_log!(
@ -968,7 +973,7 @@ fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> Vec<mixer::Ch
kAudioUnitScope_Output,
AU_OUT_BUS,
&mut size,
ptr::null_mut(),
None,
);
if rv != NO_ERR {
cubeb_log!(
@ -1226,6 +1231,7 @@ fn set_buffer_size(
}
}
#[allow(clippy::mutex_atomic)] // The mutex needs to be fed into Condvar::wait_timeout.
fn set_buffer_size_sync(unit: AudioUnit, devtype: DeviceType, frames: u32) -> 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<ffi::cubeb_device> = 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)) })

View File

@ -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();

View File

@ -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));

View File

@ -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::<UInt32>();
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::<UInt32>()
&mut size
),
NO_ERR
);

View File

@ -5,18 +5,14 @@
use cubeb_backend::SampleFormat as fmt;
use std::mem;
pub fn allocate_array_by_size<T>(size: usize) -> Vec<T> {
pub fn allocate_array_by_size<T: Clone + Default>(size: usize) -> Vec<T> {
assert_eq!(size % mem::size_of::<T>(), 0);
let elements = size / mem::size_of::<T>();
allocate_array::<T>(elements)
}
pub fn allocate_array<T>(elements: usize) -> Vec<T> {
let mut array = Vec::<T>::with_capacity(elements);
unsafe {
array.set_len(elements);
}
array
pub fn allocate_array<T: Clone + Default>(elements: usize) -> Vec<T> {
vec![T::default(); elements]
}
pub fn forget_vec<T>(v: Vec<T>) -> (*mut T, usize) {
@ -43,7 +39,9 @@ struct Finalizer<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> Drop for Finalizer<F> {
fn drop(&mut self) {
self.0.take().map(|f| f());
if let Some(f) = self.0.take() {
f()
}
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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"}

37
third_party/rust/float-cmp/Cargo.toml vendored Normal file
View File

@ -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 <mike@mikedilger.com>"]
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"]

19
third_party/rust/float-cmp/LICENSE vendored Normal file
View File

@ -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.

143
third_party/rust/float-cmp/README.md vendored Normal file
View File

@ -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<F> {
pub x: F,
pub y: F,
}
impl<'a, M: Copy, F: Copy + ApproxEq<Margin=M>> ApproxEq for &'a Vec2<F> {
type Margin = M;
fn approx_eq<T: Into<Self::Margin>>(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/)

284
third_party/rust/float-cmp/src/eq.rs vendored Normal file
View File

@ -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<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool;
/// This method tests for `self` and `other` values to be not approximately
/// equal within `margin`.
fn approx_ne<M: Into<Self::Margin>>(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<M: Into<Self::Margin>>(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<M: Into<Self::Margin>>(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);
}

196
third_party/rust/float-cmp/src/lib.rs vendored Normal file
View File

@ -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<F> {
//! pub x: F,
//! pub y: F,
//! }
//!
//! impl<'a, M: Copy + Default, F: Copy + ApproxEq<Margin=M>> ApproxEq for &'a Vec2<F> {
//! type Margin = M;
//!
//! fn approx_eq<T: Into<Self::Margin>>(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;

View File

@ -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()) );
}

142
third_party/rust/float-cmp/src/ratio.rs vendored Normal file
View File

@ -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<Output = Self> + Sub<Output = Self> + Neg<Output = Self>
+ 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);
}

240
third_party/rust/float-cmp/src/ulps.rs vendored Normal file
View File

@ -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) -> <Self as Ulps>::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) -> <Self as Ulps>::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 = <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 = <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);
}

View File

@ -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: <Self::Flt as 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: <Self::Flt as 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);
}

View File

@ -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" }