Skip to content

Commit 3e06315

Browse files
committed
Merge branch 'master' into dual-pivot
2 parents 2b1b502 + f830c9e commit 3e06315

File tree

6 files changed

+186
-44
lines changed

6 files changed

+186
-44
lines changed

.github/workflows/ci.yml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: Continuous integration
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
RUSTFLAGS: "-D warnings"
12+
13+
jobs:
14+
15+
test:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
rust:
20+
- stable
21+
- beta
22+
- nightly
23+
- 1.49.0 # MSRV
24+
steps:
25+
- uses: actions/checkout@v2
26+
- uses: actions-rs/toolchain@v1
27+
with:
28+
profile: minimal
29+
toolchain: ${{ matrix.rust }}
30+
override: true
31+
- name: Build
32+
run: cargo build --verbose
33+
- name: Run tests
34+
run: cargo test --verbose
35+
36+
cross_test:
37+
runs-on: ubuntu-latest
38+
strategy:
39+
matrix:
40+
include:
41+
# 64-bit, big-endian
42+
- rust: stable
43+
target: mips64-unknown-linux-gnuabi64
44+
# 32-bit, big-endian
45+
- rust: stable
46+
target: mips-unknown-linux-gnu
47+
# 32-bit, little-endian
48+
- rust: stable
49+
target: i686-unknown-linux-gnu
50+
steps:
51+
- uses: actions/checkout@v2
52+
- uses: actions-rs/toolchain@v1
53+
with:
54+
profile: minimal
55+
toolchain: ${{ matrix.rust }}
56+
target: ${{ matrix.target }}
57+
override: true
58+
- name: Install cross
59+
run: cargo install cross -f
60+
- name: Build
61+
run: cross build --verbose --target=${{ matrix.target }}
62+
- name: Run tests
63+
run: cross test --verbose --target=${{ matrix.target }}
64+
65+
format:
66+
runs-on: ubuntu-latest
67+
strategy:
68+
matrix:
69+
rust:
70+
- stable
71+
steps:
72+
- uses: actions/checkout@v2
73+
- uses: actions-rs/toolchain@v1
74+
with:
75+
profile: minimal
76+
toolchain: ${{ matrix.rust }}
77+
override: true
78+
components: rustfmt
79+
- name: Rustfmt
80+
run: cargo fmt -- --check
81+
82+
coverage:
83+
runs-on: ubuntu-latest
84+
strategy:
85+
matrix:
86+
rust:
87+
- nightly
88+
steps:
89+
- uses: actions/checkout@v2
90+
- uses: actions-rs/toolchain@v1
91+
with:
92+
profile: minimal
93+
toolchain: ${{ matrix.rust }}
94+
override: true
95+
- name: Install tarpaulin
96+
run: cargo install cargo-tarpaulin -f
97+
- name: Generate code coverage
98+
run: cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml
99+
- name: Upload to codecov.io
100+
uses: codecov/codecov-action@v1
101+
with:
102+
fail_ci_if_error: true

.travis.yml

Lines changed: 0 additions & 35 deletions
This file was deleted.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ndarray-stats"
3-
version = "0.5.0"
3+
version = "0.5.1"
44
authors = ["Jim Turner <[email protected]>", "LukeMathWalker <[email protected]>"]
55
edition = "2018"
66

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# ndarray-stats
22

3-
[![Build status](https://travis-ci.org/rust-ndarray/ndarray-stats.svg?branch=master)](https://travis-ci.org/rust-ndarray/ndarray-stats)
43
[![Coverage](https://codecov.io/gh/rust-ndarray/ndarray-stats/branch/master/graph/badge.svg)](https://codecov.io/gh/rust-ndarray/ndarray-stats)
54
[![Dependencies status](https://deps.rs/repo/github/rust-ndarray/ndarray-stats/status.svg)](https://deps.rs/repo/github/rust-ndarray/ndarray-stats)
65
[![Crate](https://img.shields.io/crates/v/ndarray-stats.svg)](https://crates.io/crates/ndarray-stats)
@@ -28,11 +27,21 @@ Please feel free to contribute new functionality! A roadmap can be found [here](
2827
```toml
2928
[dependencies]
3029
ndarray = "0.15"
31-
ndarray-stats = "0.5"
30+
ndarray-stats = "0.5.1"
3231
```
3332

3433
## Releases
3534

35+
* **0.5.1**
36+
* Fixed bug in implementation of `MaybeNaN::remove_nan_mut` for `f32` and
37+
`f64` for views with non-standard layouts. Before this fix, the bug could
38+
cause incorrect results, buffer overflows, etc., in this method and others
39+
which use it. Thanks to [@JacekCzupyt](https://github.com/JacekCzupyt) for
40+
reporting the issue (#89).
41+
* Minor docs improvements.
42+
43+
*Contributors*: [@jturner314](https://github.com/jturner314), [@BenMoon](https://github.com/BenMoon)
44+
3645
* **0.5.0**
3746
* Breaking changes
3847
* Minimum supported Rust version: `1.49.0`
@@ -109,7 +118,7 @@ Please feel free to create issues and submit PRs.
109118

110119
## License
111120

112-
Copyright 2018 `ndarray-stats` developers
121+
Copyright 2018–2022 `ndarray-stats` developers
113122

114123
Licensed under the [Apache License, Version 2.0](LICENSE-APACHE), or the [MIT
115124
license](LICENSE-MIT), at your option. You may not use this project except in

src/maybe_nan/mod.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use ndarray::prelude::*;
22
use ndarray::{s, Data, DataMut, RemoveAxis};
33
use noisy_float::types::{N32, N64};
4+
use std::mem;
45

56
/// A number type that can have not-a-number values.
67
pub trait MaybeNan: Sized {
@@ -69,6 +70,42 @@ fn remove_nan_mut<A: MaybeNan>(mut view: ArrayViewMut1<'_, A>) -> ArrayViewMut1<
6970
}
7071
}
7172

73+
/// Casts a view from one element type to another.
74+
///
75+
/// # Panics
76+
///
77+
/// Panics if `T` and `U` differ in size or alignment.
78+
///
79+
/// # Safety
80+
///
81+
/// The caller must ensure that qll elements in `view` are valid values for type `U`.
82+
unsafe fn cast_view_mut<T, U>(mut view: ArrayViewMut1<'_, T>) -> ArrayViewMut1<'_, U> {
83+
assert_eq!(mem::size_of::<T>(), mem::size_of::<U>());
84+
assert_eq!(mem::align_of::<T>(), mem::align_of::<U>());
85+
let ptr: *mut U = view.as_mut_ptr().cast();
86+
let len: usize = view.len_of(Axis(0));
87+
let stride: isize = view.stride_of(Axis(0));
88+
if len <= 1 {
89+
// We can use a stride of `0` because the stride is irrelevant for the `len == 1` case.
90+
let stride = 0;
91+
ArrayViewMut1::from_shape_ptr([len].strides([stride]), ptr)
92+
} else if stride >= 0 {
93+
let stride = stride as usize;
94+
ArrayViewMut1::from_shape_ptr([len].strides([stride]), ptr)
95+
} else {
96+
// At this point, stride < 0. We have to construct the view by using the inverse of the
97+
// stride and then inverting the axis, since `ArrayViewMut::from_shape_ptr` requires the
98+
// stride to be nonnegative.
99+
let neg_stride = stride.checked_neg().unwrap() as usize;
100+
// This is safe because `ndarray` guarantees that it's safe to offset the
101+
// pointer anywhere in the array.
102+
let neg_ptr = ptr.offset((len - 1) as isize * stride);
103+
let mut v = ArrayViewMut1::from_shape_ptr([len].strides([neg_stride]), neg_ptr);
104+
v.invert_axis(Axis(0));
105+
v
106+
}
107+
}
108+
72109
macro_rules! impl_maybenan_for_fxx {
73110
($fxx:ident, $Nxx:ident) => {
74111
impl MaybeNan for $fxx {
@@ -102,11 +139,9 @@ macro_rules! impl_maybenan_for_fxx {
102139

103140
fn remove_nan_mut(view: ArrayViewMut1<'_, $fxx>) -> ArrayViewMut1<'_, $Nxx> {
104141
let not_nan = remove_nan_mut(view);
105-
// This is safe because `remove_nan_mut` has removed the NaN
106-
// values, and `$Nxx` is a thin wrapper around `$fxx`.
107-
unsafe {
108-
ArrayViewMut1::from_shape_ptr(not_nan.dim(), not_nan.as_ptr() as *mut $Nxx)
109-
}
142+
// This is safe because `remove_nan_mut` has removed the NaN values, and `$Nxx` is
143+
// a thin wrapper around `$fxx`.
144+
unsafe { cast_view_mut(not_nan) }
110145
}
111146
}
112147
};

tests/maybe_nan.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use ndarray::prelude::*;
2+
use ndarray_stats::MaybeNan;
3+
use noisy_float::types::{n64, N64};
4+
5+
#[test]
6+
fn remove_nan_mut_nonstandard_layout() {
7+
fn eq_unordered(mut a: Vec<N64>, mut b: Vec<N64>) -> bool {
8+
a.sort();
9+
b.sort();
10+
a == b
11+
}
12+
let a = aview1(&[1., 2., f64::NAN, f64::NAN, 3., f64::NAN, 4., 5.]);
13+
{
14+
let mut a = a.to_owned();
15+
let v = f64::remove_nan_mut(a.slice_mut(s![..;2]));
16+
assert!(eq_unordered(v.to_vec(), vec![n64(1.), n64(3.), n64(4.)]));
17+
}
18+
{
19+
let mut a = a.to_owned();
20+
let v = f64::remove_nan_mut(a.slice_mut(s![..;-1]));
21+
assert!(eq_unordered(
22+
v.to_vec(),
23+
vec![n64(5.), n64(4.), n64(3.), n64(2.), n64(1.)],
24+
));
25+
}
26+
{
27+
let mut a = a.to_owned();
28+
let v = f64::remove_nan_mut(a.slice_mut(s![..;-2]));
29+
assert!(eq_unordered(v.to_vec(), vec![n64(5.), n64(2.)]));
30+
}
31+
}

0 commit comments

Comments
 (0)