Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 56 additions & 56 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ name: cargo
on:
push:
branches:
- main
- main
pull_request:
branches:
- main
- main

env:
CARGO_TERM_COLOR: always

jobs:
pre_job:
continue-on-error: true
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
# All of these options are optional, so you can remove them if you are happy with the defaults
concurrent_skipping: "same_content_newer"
skip_after_successful_duplicate: "true"
paths_ignore: '["**/README.md"]'
do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]'
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
# All of these options are optional, so you can remove them if you are happy with the defaults
concurrent_skipping: "same_content_newer"
skip_after_successful_duplicate: "true"
paths_ignore: '["**/README.md"]'
do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]'

build:
name: Build on Ubuntu
Expand All @@ -34,30 +34,30 @@ jobs:
runs-on: ubuntu-26.04

steps:
- uses: actions/checkout@v4
- name: Prepare environment
run: |
sudo apt-get update
sudo apt-get install clang
sudo apt-get install libclang1
sudo apt-get install libeccodes-dev
rustup update stable
cargo install cargo-criterion
cargo clean
- name: Check release build
run: |
cargo build --release --no-default-features
cargo build --release --features "ndarray"
- name: Check with clippy
run: |
cargo clippy --features "ndarray" -- -D warnings
- name: Check tests
run: |
cargo test --no-default-features
cargo test --features "ndarray"
- name: Benchmark with criterion
run: |
cargo criterion
- uses: actions/checkout@v4
- name: Prepare environment
run: |
sudo apt-get update
sudo apt-get install clang
sudo apt-get install libclang-dev
sudo apt-get install libeccodes-dev
rustup update stable
cargo install cargo-criterion
cargo clean
- name: Check release build
run: |
cargo build --release --no-default-features
cargo build --release --features "ndarray"
- name: Check with clippy
run: |
cargo clippy --features "ndarray" -- -D warnings
- name: Check tests
run: |
cargo test --no-default-features
cargo test --features "ndarray"
- name: Benchmark with criterion
run: |
cargo criterion

build-macos:
name: Build on MacOS
Expand All @@ -66,24 +66,24 @@ jobs:
runs-on: macos-latest

steps:
- uses: actions/checkout@v4
- name: Prepare environment
run: |
brew install eccodes
rustup update stable
cargo install cargo-criterion
cargo clean
- name: Check release build
run: |
cargo build --release --no-default-features
cargo build --release --features "ndarray"
- name: Check with clippy
run: |
cargo clippy --features "ndarray" -- -D warnings
- name: Check tests
run: |
cargo test --no-default-features
cargo test --features "ndarray"
- name: Benchmark with criterion
run: |
cargo criterion
- uses: actions/checkout@v4
- name: Prepare environment
run: |
brew install eccodes
rustup update stable
cargo install cargo-criterion
cargo clean
- name: Check release build
run: |
cargo build --release --no-default-features
cargo build --release --features "ndarray"
- name: Check with clippy
run: |
cargo clippy --features "ndarray" -- -D warnings
- name: Check tests
run: |
cargo test --no-default-features
cargo test --features "ndarray"
- name: Benchmark with criterion
run: |
cargo criterion
38 changes: 35 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "eccodes"
description = "Unofficial high-level Rust bindings of the latest ecCodes release"
repository = "https://github.com/ScaleWeather/eccodes"
version = "0.14.0"
version = "0.15.0"
readme = "README.md"
authors = ["Jakub Lewandowski <scaleweather@gmail.com>"]
keywords = ["eccodes", "grib", "bufr", "meteorology", "weather"]
Expand All @@ -23,7 +23,7 @@ eccodes-sys = { version = "0.7.0", default-features = false }
libc = { version = "0.2", default-features = false }
thiserror = { version = "2.0", default-features = false }
errno = { version = "0.3", default-features = false }
num-derive = { version = "0.4", default-features = false }
num-derive = { version = "0.5", default-features = false }
num-traits = { version = "0.2", default-features = false }
fallible-iterator = { version = "0.3", default-features = false }
ndarray = { version = "0.17", default-features = false, optional = true, features = [
Expand All @@ -38,7 +38,7 @@ tracing = { version = "0.1", default-features = false, features = [
[dev-dependencies]
reqwest = { version = "0.13", features = ["rustls"] }
criterion = "0.8"
rand = "0.9"
rand = "0.10"
anyhow = { version = "1.0", features = ["backtrace"] }
float-cmp = "0.10"

Expand All @@ -53,3 +53,35 @@ features = ["docs", "ndarray"]
[[bench]]
name = "main"
harness = false

[lints.rust]
warnings = "deny"
missing_docs = "deny"

[lints.clippy]
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
perf = { level = "deny", priority = -1 }
cargo = { level = "deny", priority = -1 }

unwrap_used = "deny"
expect_used = "deny"
todo = "deny"
indexing_slicing = "deny"
unreachable = "deny"
unimplemented = "deny"
panic = "deny"
exit = "deny"

arithmetic_side_effects = "deny"
unchecked_time_subtraction = "deny"
string_slice = "deny"
panic_in_result_fn = "deny"
dbg_macro = "deny"
unnecessary_self_imports = "deny"
absolute_paths = "deny"
doc_lazy_continuation = "deny"

as_conversions = "allow"
multiple_crate_versions = "allow"
cast_possible_wrap = "allow"
49 changes: 21 additions & 28 deletions benches/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(missing_docs)]
use criterion::{Criterion, criterion_group, criterion_main};
use eccodes::{FallibleIterator, KeyRead};
use eccodes::codes_file::{CodesFile, ProductKind};
use eccodes::{FallibleIterator, KeyRead};
use std::hint::black_box;
use std::path::Path;

Expand All @@ -14,48 +15,40 @@ pub fn key_reading(c: &mut Criterion) {
let msg = handle.ref_message_iter().next().unwrap().unwrap();

c.bench_function("long reading", |b| {
b.iter(|| msg.read_key_dynamic(black_box("dataDate")).unwrap())
b.iter(|| -> i64 { msg.read_key(black_box("dataDate")).unwrap() })
});

c.bench_function("double reading", |b| {
b.iter(|| {
msg.read_key_dynamic(black_box("jDirectionIncrementInDegrees"))
c.bench_function("float reading", |b| {
b.iter(|| -> f32 {
msg.read_key(black_box("jDirectionIncrementInDegrees"))
.unwrap()
})
});

c.bench_function("double array reading", |b| {
b.iter(|| msg.read_key_dynamic(black_box("values")).unwrap())
});

c.bench_function("static double array reading", |b| {
b.iter(|| -> Vec<f64> { msg.read_key(black_box("values")).unwrap() })
});

c.bench_function("static float array reading", |b| {
b.iter(|| -> Vec<f32> { msg.read_key(black_box("values")).unwrap() })
c.bench_function("double reading", |b| {
b.iter(|| -> f64 {
msg.read_key(black_box("jDirectionIncrementInDegrees"))
.unwrap()
})
});

c.bench_function("string reading", |b| {
b.iter(|| msg.read_key_dynamic(black_box("name")).unwrap())
b.iter(|| -> String { msg.read_key(black_box("name")).unwrap() })
});

c.bench_function("bytes reading", |b| {
b.iter(|| msg.read_key_dynamic(black_box("section1Padding")).unwrap())
});

c.bench_function("missing nul-byte termination reading", |b| {
b.iter(|| {
msg.read_key_dynamic(black_box("experimentVersionNumber"))
c.bench_function("long array reading", |b| {
b.iter(|| -> Vec<i64> {
msg.read_key(black_box("numberOfPointsAlongAParallel"))
.unwrap()
})
});

c.bench_function("problematic key reading", |b| {
b.iter(|| {
msg.read_key_dynamic(black_box("zero"))
.unwrap_or_else(|_| msg.read_key_dynamic(black_box("zeros")).unwrap())
})
c.bench_function("float array reading", |b| {
b.iter(|| -> Vec<f32> { msg.read_key(black_box("values")).unwrap() })
});

c.bench_function("double array reading", |b| {
b.iter(|| -> Vec<f64> { msg.read_key(black_box("values")).unwrap() })
});
}

Expand Down
4 changes: 4 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
allow-panic-in-tests = true
allow-indexing-slicing-in-tests = true
5 changes: 4 additions & 1 deletion src/codes_file/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,13 @@ impl<D: Debug> FallibleIterator for ArcMessageIter<D> {
/// This method internally uses a Mutex to access `CodesFile`, which can panic when poisoned,
/// but thers is no path in which you can get to the state of poisoned mutex, while still able to access this method.
fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
#[allow(
clippy::expect_used,
reason = "This mutex can be poisoned only when thread that holds ArcMessageIter panics, which would make using the mutex impossible"
)]
let eccodes_handle = self
.codes_file
.lock()
// This mutex can be poisoned only when thread that holds ArcMessageIter panics, which would make using the mutex impossible")
.expect("The mutex inside ArcMessageIter got poisoned")
.generate_codes_handle()?;

Expand Down
4 changes: 4 additions & 0 deletions src/codes_message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ pub type RefMessage<'ch> = CodesMessage<RefParent<'ch>>;
/// [`CodesMessage`] that can be moved and shared across threads.
pub type ArcMessage<D> = CodesMessage<ArcParent<D>>;

#[allow(
clippy::non_send_fields_in_send_ty,
reason = "Safety is ensured on the C side"
)]
unsafe impl<D: Debug> Send for ArcMessage<D> {}
unsafe impl<D: Debug> Sync for ArcMessage<D> {}

Expand Down
8 changes: 6 additions & 2 deletions src/codes_message/ndarray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl<P: Debug> CodesMessage<P> {

let vals: Vec<f64> = self.read_key("values")?;

let expected_vals_len = ni * nj;
let expected_vals_len = ni.checked_mul(nj).ok_or(CodesError::TooMuchValues)?;
if vals.len() != expected_vals_len {
return Err(
MessageNdarrayError::UnexpectedValuesLength(vals.len(), expected_vals_len).into(),
Expand Down Expand Up @@ -100,7 +100,11 @@ impl<P: Debug> CodesMessage<P> {

let latlonvals: Vec<f64> = self.read_key("latLonValues")?;

let expected_vals_len = ni * nj * 3;
let expected_vals_len = ni
.checked_mul(nj)
.ok_or(CodesError::TooMuchValues)?
.checked_mul(3)
.ok_or(CodesError::TooMuchValues)?;
if latlonvals.len() != expected_vals_len {
return Err(MessageNdarrayError::UnexpectedValuesLength(
latlonvals.len(),
Expand Down
Loading
Loading