Skip to content

Commit 8909556

Browse files
committed
WIP mostly finish CodesIndex API and conversion to CH
1 parent db3555c commit 8909556

File tree

6 files changed

+319
-128
lines changed

6 files changed

+319
-128
lines changed

src/codes_handle/iterator.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::{
1010
},
1111
};
1212

13+
use super::GribFile;
14+
1315
///`FallibleIterator` implementation for `CodesHandle` to access GRIB messages inside file.
1416
///
1517
///To access GRIB messages the ecCodes library uses a method similar to a C-style iterator.
@@ -77,23 +79,23 @@ use crate::{
7779
///## Errors
7880
///The `next()` method will return [`CodesInternal`](crate::errors::CodesInternal)
7981
///when internal ecCodes function returns non-zero code.
80-
impl FallibleIterator for CodesHandle {
82+
impl FallibleIterator for CodesHandle<GribFile> {
8183
type Item = KeyedMessage;
8284

8385
type Error = CodesError;
8486

8587
fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
8688
let file_handle;
8789
unsafe {
88-
codes_handle_delete(self.file_handle)?;
89-
file_handle = codes_handle_new_from_file(self.file_pointer, self.product_kind);
90+
codes_handle_delete(self.eccodes_handle)?;
91+
file_handle = codes_handle_new_from_file(self.source.pointer, self.product_kind);
9092
}
9193

9294
match file_handle {
9395
Ok(h) => {
94-
self.file_handle = h;
96+
self.eccodes_handle = h;
9597

96-
if self.file_handle.is_null() {
98+
if self.eccodes_handle.is_null() {
9799
Ok(None)
98100
} else {
99101
let message = get_message_from_handle(h);

src/codes_handle/keyed_message/mod.rs

+1-16
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::{
1414
codes_grib_nearest_new, codes_handle_delete, codes_handle_new_from_message_copy,
1515
codes_keys_iterator_delete,
1616
},
17-
CodesHandle,
1817
};
1918

2019
use super::{KeysIteratorFlags, NearestGridpoint};
@@ -123,7 +122,7 @@ impl Drop for KeyedMessage {
123122
///Technical note: delete functions in ecCodes can only fail with [`CodesInternalError`](crate::errors::CodesInternal::CodesInternalError)
124123
///when other functions corrupt the inner memory of pointer, in that case memory leak is possible.
125124
///In case of corrupt pointer segmentation fault will occur.
126-
///The pointers are cleared at the end of drop as they ar not not functional despite the result of delete functions.
125+
///The pointers are cleared at the end of drop as they are not functional despite the result of delete functions.
127126
fn drop(&mut self) {
128127
if let Some(nrst) = self.nearest_handle {
129128
unsafe {
@@ -161,20 +160,6 @@ impl Drop for KeyedMessage {
161160
}
162161
}
163162

164-
impl TryFrom<CodesHandle> for KeyedMessage {
165-
type Error = CodesError;
166-
fn try_from(value: CodesHandle) -> Result<Self, CodesError> {
167-
Ok(KeyedMessage {
168-
message_handle: value.file_handle,
169-
iterator_flags: None,
170-
iterator_namespace: None,
171-
keys_iterator: None,
172-
keys_iterator_next_item_exists: false,
173-
nearest_handle: None,
174-
})
175-
}
176-
}
177-
178163
#[cfg(test)]
179164
mod tests {
180165
use crate::codes_handle::{CodesHandle, ProductKind};

src/codes_handle/mod.rs

+137-45
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
44
#[cfg(feature = "ec_index")]
55
use crate::{
6-
codes_index::CodesIndex, intermediate_bindings::codes_handle_new_from_index,
6+
codes_index::CodesIndex,
7+
intermediate_bindings::codes_index::{codes_handle_new_from_index, codes_index_delete},
78
};
8-
use crate::errors::CodesError;
9+
use crate::{errors::CodesError, intermediate_bindings::codes_handle_delete};
910
use bytes::Bytes;
1011
use eccodes_sys::{codes_handle, codes_keys_iterator, codes_nearest, ProductKind_PRODUCT_GRIB};
1112
use errno::errno;
1213
use libc::{c_char, c_void, size_t, FILE};
1314
use log::warn;
1415
use std::{
16+
fmt::Debug,
1517
fs::{File, OpenOptions},
1618
os::unix::prelude::AsRawFd,
1719
path::Path,
@@ -28,14 +30,20 @@ use eccodes_sys::{
2830
mod iterator;
2931
mod keyed_message;
3032

33+
#[derive(Debug)]
34+
#[doc(hidden)]
35+
pub struct GribFile {
36+
pointer: *mut FILE,
37+
}
38+
3139
///Main structure used to operate on the GRIB file.
3240
///It takes a full ownership of the accessed file.
3341
///It can be constructed either using a file or a memory buffer.
3442
#[derive(Debug)]
35-
pub struct CodesHandle {
36-
file_handle: *mut codes_handle,
43+
pub struct CodesHandle<SOURCE: Debug + SpecialDrop> {
44+
eccodes_handle: *mut codes_handle,
3745
_data: DataContainer,
38-
file_pointer: *mut FILE,
46+
source: SOURCE,
3947
product_kind: ProductKind,
4048
}
4149

@@ -113,6 +121,8 @@ pub enum KeysIteratorFlags {
113121
enum DataContainer {
114122
FileBytes(Bytes),
115123
FileBuffer(File),
124+
#[cfg(feature = "ec_index")]
125+
Empty(),
116126
}
117127

118128
///Enum representing the kind of product (file type) inside handled file.
@@ -138,7 +148,7 @@ pub struct NearestGridpoint {
138148
pub value: f64,
139149
}
140150

141-
impl CodesHandle {
151+
impl CodesHandle<GribFile> {
142152
///The constructor that takes a [`path`](Path) to an existing file and
143153
///a requested [`ProductKind`] and returns the [`CodesHandle`] object.
144154
///
@@ -184,8 +194,10 @@ impl CodesHandle {
184194

185195
Ok(CodesHandle {
186196
_data: (DataContainer::FileBuffer(file)),
187-
file_handle,
188-
file_pointer,
197+
eccodes_handle: file_handle,
198+
source: GribFile {
199+
pointer: file_pointer,
200+
},
189201
product_kind,
190202
})
191203
}
@@ -238,13 +250,39 @@ impl CodesHandle {
238250

239251
Ok(CodesHandle {
240252
_data: (DataContainer::FileBytes(file_data)),
241-
file_handle,
242-
file_pointer,
253+
eccodes_handle: file_handle,
254+
source: GribFile {
255+
pointer: file_pointer,
256+
},
243257
product_kind,
244258
})
245259
}
246260
}
247261

262+
#[cfg(feature = "ec_index")]
263+
#[cfg_attr(docsrs, doc(cfg(feature = "ec_index")))]
264+
impl CodesHandle<CodesIndex> {
265+
pub fn new_from_index(
266+
index: CodesIndex,
267+
product_kind: ProductKind,
268+
) -> Result<Self, CodesError> {
269+
let handle: *mut codes_handle;
270+
271+
unsafe {
272+
handle = codes_handle_new_from_index(index.pointer)?;
273+
}
274+
275+
let new_handle = CodesHandle {
276+
_data: DataContainer::Empty(), //unused, index owns data
277+
eccodes_handle: handle,
278+
source: index,
279+
product_kind,
280+
};
281+
282+
Ok(new_handle)
283+
}
284+
}
285+
248286
fn open_with_fdopen(file: &File) -> Result<*mut FILE, CodesError> {
249287
let file_ptr;
250288
unsafe {
@@ -279,26 +317,26 @@ fn open_with_fmemopen(file_data: &Bytes) -> Result<*mut FILE, CodesError> {
279317
Ok(file_ptr)
280318
}
281319

282-
impl Drop for CodesHandle {
283-
///Executes the destructor for this type.
284-
///This method calls `fclose()` from libc for graceful cleanup.
285-
///
286-
///Currently it is assumed that under normal circumstances this destructor never fails.
287-
///However in some edge cases fclose can return non-zero code.
288-
///In such case all pointers and file descriptors are safely deleted.
289-
///However memory leaks can still occur.
290-
///
291-
///If any function called in the destructor returns an error warning will appear in log.
292-
///If bugs occurs during `CodesHandle` drop please enable log output and post issue on [Github](https://github.com/ScaleWeather/eccodes).
293-
fn drop(&mut self) {
320+
/// This trait is neccessary because (1) drop in GribFile/IndexFile cannot
321+
/// be called directly as source cannot be moved out of shared reference
322+
/// and (2) Drop drops fields in arbitrary order leading to fclose() failing
323+
#[doc(hidden)]
324+
pub trait SpecialDrop {
325+
fn spec_drop(&mut self);
326+
}
327+
328+
impl SpecialDrop for GribFile {
329+
fn spec_drop(&mut self) {
330+
dbg!("GribFile drop");
331+
294332
//fclose() can fail in several different cases, however there is not much
295333
//that we can nor we should do about it. the promise of fclose() is that
296334
//the stream will be disassociated from the file after the call, therefore
297335
//use of stream after the call to fclose() is undefined behaviour, so we clear it
298336
let return_code;
299337
unsafe {
300-
if !self.file_pointer.is_null() {
301-
return_code = libc::fclose(self.file_pointer);
338+
if !self.pointer.is_null() {
339+
return_code = libc::fclose(self.pointer);
302340
if return_code != 0 {
303341
let error_val = errno();
304342
warn!(
@@ -310,35 +348,62 @@ impl Drop for CodesHandle {
310348
}
311349
}
312350

313-
self.file_pointer = null_mut();
351+
self.pointer = null_mut();
314352
}
315353
}
316354

317355
#[cfg(feature = "ec_index")]
318-
#[cfg_attr(docsrs, doc(cfg(feature = "ec_index")))]
319-
impl TryFrom<&CodesIndex> for CodesHandle {
320-
type Error = CodesError;
321-
fn try_from(value: &CodesIndex) -> Result<Self, CodesError> {
322-
let handle: *mut codes_handle;
356+
impl SpecialDrop for CodesIndex {
357+
fn spec_drop(&mut self) {
358+
dbg!("CodesIndex drop");
359+
360+
if self.pointer.is_null() {
361+
return;
362+
}
363+
323364
unsafe {
324-
handle = codes_handle_new_from_index(value.index_handle)?;
365+
codes_index_delete(self.pointer);
325366
}
326367

327-
let new_handle = CodesHandle {
328-
_data: DataContainer::FileBytes(Bytes::new()), //unused, index owns data
329-
file_pointer: null_mut(),
330-
file_handle: handle,
331-
product_kind: ProductKind::GRIB,
332-
};
333-
Ok(new_handle)
368+
self.pointer = null_mut();
369+
}
370+
}
371+
372+
impl<S: Debug + SpecialDrop> Drop for CodesHandle<S> {
373+
///Executes the destructor for this type.
374+
///This method calls `fclose()` from libc for graceful cleanup.
375+
///
376+
///Currently it is assumed that under normal circumstances this destructor never fails.
377+
///However in some edge cases fclose can return non-zero code.
378+
///In such case all pointers and file descriptors are safely deleted.
379+
///However memory leaks can still occur.
380+
///
381+
///If any function called in the destructor returns an error warning will appear in log.
382+
///If bugs occurs during `CodesHandle` drop please enable log output and post issue on [Github](https://github.com/ScaleWeather/eccodes).
383+
fn drop(&mut self) {
384+
dbg!("CodesHandle drop");
385+
386+
unsafe {
387+
codes_handle_delete(self.eccodes_handle).unwrap_or_else(|error| {
388+
warn!("codes_handle_delete() returned an error: {:?}", &error);
389+
});
390+
}
391+
392+
self.eccodes_handle = null_mut();
393+
394+
self.source.spec_drop();
334395
}
335396
}
336397

337398
#[cfg(test)]
338399
mod tests {
339400
use eccodes_sys::ProductKind_PRODUCT_GRIB;
340401

341-
use crate::codes_handle::{CodesHandle, DataContainer, ProductKind};
402+
use crate::{
403+
codes_handle::{CodesHandle, DataContainer, ProductKind},
404+
codes_index::Select,
405+
CodesIndex,
406+
};
342407
use log::Level;
343408
use std::path::Path;
344409

@@ -349,13 +414,13 @@ mod tests {
349414

350415
let handle = CodesHandle::new_from_file(file_path, product_kind).unwrap();
351416

352-
assert!(!handle.file_pointer.is_null());
353-
assert!(handle.file_handle.is_null());
417+
assert!(!handle.source.pointer.is_null());
418+
assert!(handle.eccodes_handle.is_null());
354419
assert_eq!(handle.product_kind as u32, { ProductKind_PRODUCT_GRIB });
355420

356421
let metadata = match &handle._data {
357-
DataContainer::FileBytes(_) => panic!(),
358422
DataContainer::FileBuffer(file) => file.metadata().unwrap(),
423+
_ => panic!(),
359424
};
360425

361426
println!("{:?}", metadata);
@@ -374,16 +439,38 @@ mod tests {
374439
.unwrap();
375440

376441
let handle = CodesHandle::new_from_memory(file_data, product_kind).unwrap();
377-
assert!(!handle.file_pointer.is_null());
378-
assert!(handle.file_handle.is_null());
442+
assert!(!handle.source.pointer.is_null());
443+
assert!(handle.eccodes_handle.is_null());
379444
assert_eq!(handle.product_kind as u32, { ProductKind_PRODUCT_GRIB });
380445

381446
match &handle._data {
382447
DataContainer::FileBytes(file) => assert!(!file.is_empty()),
383-
DataContainer::FileBuffer(_) => panic!(),
448+
_ => panic!(),
384449
};
385450
}
386451

452+
#[test]
453+
fn index_constructor_and_destructor() {
454+
let file_path = Path::new("./data/iceland-surface.idx");
455+
let index = CodesIndex::read_from_file(file_path)
456+
.unwrap()
457+
.select("shortName", "2t")
458+
.unwrap()
459+
.select("typeOfLevel", "surface")
460+
.unwrap()
461+
.select("level", 0)
462+
.unwrap()
463+
.select("stepType", "instant")
464+
.unwrap();
465+
466+
let i_ptr = index.pointer.clone();
467+
468+
let handle = CodesHandle::new_from_index(index, ProductKind::GRIB).unwrap();
469+
470+
assert_eq!(handle.source.pointer, i_ptr);
471+
assert!(!handle.eccodes_handle.is_null());
472+
}
473+
387474
#[tokio::test]
388475
async fn codes_handle_drop() {
389476
testing_logger::setup();
@@ -396,6 +483,11 @@ mod tests {
396483
drop(handle);
397484

398485
testing_logger::validate(|captured_logs| {
486+
for cl in captured_logs {
487+
let b = &cl.body;
488+
489+
println!("{:?}", b);
490+
}
399491
assert_eq!(captured_logs.len(), 0);
400492
});
401493
}

0 commit comments

Comments
 (0)