3
3
4
4
#[ cfg( feature = "ec_index" ) ]
5
5
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} ,
7
8
} ;
8
- use crate :: errors:: CodesError ;
9
+ use crate :: { errors:: CodesError , intermediate_bindings :: codes_handle_delete } ;
9
10
use bytes:: Bytes ;
10
11
use eccodes_sys:: { codes_handle, codes_keys_iterator, codes_nearest, ProductKind_PRODUCT_GRIB } ;
11
12
use errno:: errno;
12
13
use libc:: { c_char, c_void, size_t, FILE } ;
13
14
use log:: warn;
14
15
use std:: {
16
+ fmt:: Debug ,
15
17
fs:: { File , OpenOptions } ,
16
18
os:: unix:: prelude:: AsRawFd ,
17
19
path:: Path ,
@@ -28,14 +30,20 @@ use eccodes_sys::{
28
30
mod iterator;
29
31
mod keyed_message;
30
32
33
+ #[ derive( Debug ) ]
34
+ #[ doc( hidden) ]
35
+ pub struct GribFile {
36
+ pointer : * mut FILE ,
37
+ }
38
+
31
39
///Main structure used to operate on the GRIB file.
32
40
///It takes a full ownership of the accessed file.
33
41
///It can be constructed either using a file or a memory buffer.
34
42
#[ 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 ,
37
45
_data : DataContainer ,
38
- file_pointer : * mut FILE ,
46
+ source : SOURCE ,
39
47
product_kind : ProductKind ,
40
48
}
41
49
@@ -113,6 +121,8 @@ pub enum KeysIteratorFlags {
113
121
enum DataContainer {
114
122
FileBytes ( Bytes ) ,
115
123
FileBuffer ( File ) ,
124
+ #[ cfg( feature = "ec_index" ) ]
125
+ Empty ( ) ,
116
126
}
117
127
118
128
///Enum representing the kind of product (file type) inside handled file.
@@ -138,7 +148,7 @@ pub struct NearestGridpoint {
138
148
pub value : f64 ,
139
149
}
140
150
141
- impl CodesHandle {
151
+ impl CodesHandle < GribFile > {
142
152
///The constructor that takes a [`path`](Path) to an existing file and
143
153
///a requested [`ProductKind`] and returns the [`CodesHandle`] object.
144
154
///
@@ -184,8 +194,10 @@ impl CodesHandle {
184
194
185
195
Ok ( CodesHandle {
186
196
_data : ( DataContainer :: FileBuffer ( file) ) ,
187
- file_handle,
188
- file_pointer,
197
+ eccodes_handle : file_handle,
198
+ source : GribFile {
199
+ pointer : file_pointer,
200
+ } ,
189
201
product_kind,
190
202
} )
191
203
}
@@ -238,13 +250,39 @@ impl CodesHandle {
238
250
239
251
Ok ( CodesHandle {
240
252
_data : ( DataContainer :: FileBytes ( file_data) ) ,
241
- file_handle,
242
- file_pointer,
253
+ eccodes_handle : file_handle,
254
+ source : GribFile {
255
+ pointer : file_pointer,
256
+ } ,
243
257
product_kind,
244
258
} )
245
259
}
246
260
}
247
261
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
+
248
286
fn open_with_fdopen ( file : & File ) -> Result < * mut FILE , CodesError > {
249
287
let file_ptr;
250
288
unsafe {
@@ -279,26 +317,26 @@ fn open_with_fmemopen(file_data: &Bytes) -> Result<*mut FILE, CodesError> {
279
317
Ok ( file_ptr)
280
318
}
281
319
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
+
294
332
//fclose() can fail in several different cases, however there is not much
295
333
//that we can nor we should do about it. the promise of fclose() is that
296
334
//the stream will be disassociated from the file after the call, therefore
297
335
//use of stream after the call to fclose() is undefined behaviour, so we clear it
298
336
let return_code;
299
337
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 ) ;
302
340
if return_code != 0 {
303
341
let error_val = errno ( ) ;
304
342
warn ! (
@@ -310,35 +348,62 @@ impl Drop for CodesHandle {
310
348
}
311
349
}
312
350
313
- self . file_pointer = null_mut ( ) ;
351
+ self . pointer = null_mut ( ) ;
314
352
}
315
353
}
316
354
317
355
#[ 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
+
323
364
unsafe {
324
- handle = codes_handle_new_from_index ( value . index_handle ) ? ;
365
+ codes_index_delete ( self . pointer ) ;
325
366
}
326
367
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 ( ) ;
334
395
}
335
396
}
336
397
337
398
#[ cfg( test) ]
338
399
mod tests {
339
400
use eccodes_sys:: ProductKind_PRODUCT_GRIB ;
340
401
341
- use crate :: codes_handle:: { CodesHandle , DataContainer , ProductKind } ;
402
+ use crate :: {
403
+ codes_handle:: { CodesHandle , DataContainer , ProductKind } ,
404
+ codes_index:: Select ,
405
+ CodesIndex ,
406
+ } ;
342
407
use log:: Level ;
343
408
use std:: path:: Path ;
344
409
@@ -349,13 +414,13 @@ mod tests {
349
414
350
415
let handle = CodesHandle :: new_from_file ( file_path, product_kind) . unwrap ( ) ;
351
416
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( ) ) ;
354
419
assert_eq ! ( handle. product_kind as u32 , { ProductKind_PRODUCT_GRIB } ) ;
355
420
356
421
let metadata = match & handle. _data {
357
- DataContainer :: FileBytes ( _) => panic ! ( ) ,
358
422
DataContainer :: FileBuffer ( file) => file. metadata ( ) . unwrap ( ) ,
423
+ _ => panic ! ( ) ,
359
424
} ;
360
425
361
426
println ! ( "{:?}" , metadata) ;
@@ -374,16 +439,38 @@ mod tests {
374
439
. unwrap ( ) ;
375
440
376
441
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( ) ) ;
379
444
assert_eq ! ( handle. product_kind as u32 , { ProductKind_PRODUCT_GRIB } ) ;
380
445
381
446
match & handle. _data {
382
447
DataContainer :: FileBytes ( file) => assert ! ( !file. is_empty( ) ) ,
383
- DataContainer :: FileBuffer ( _ ) => panic ! ( ) ,
448
+ _ => panic ! ( ) ,
384
449
} ;
385
450
}
386
451
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
+
387
474
#[ tokio:: test]
388
475
async fn codes_handle_drop ( ) {
389
476
testing_logger:: setup ( ) ;
@@ -396,6 +483,11 @@ mod tests {
396
483
drop ( handle) ;
397
484
398
485
testing_logger:: validate ( |captured_logs| {
486
+ for cl in captured_logs {
487
+ let b = & cl. body ;
488
+
489
+ println ! ( "{:?}" , b) ;
490
+ }
399
491
assert_eq ! ( captured_logs. len( ) , 0 ) ;
400
492
} ) ;
401
493
}
0 commit comments