Skip to content

Commit cad6b72

Browse files
authored
Merge pull request #173 from aldanor/feature/loc-more
Locations info continued + handle mod cleanup + downcasts
2 parents 68a0656 + 31401db commit cad6b72

20 files changed

+297
-151
lines changed

CHANGELOG.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,18 @@
4949
but must read the entire attribute at once).
5050
- Added support in `hdf5-sys` for the new functions in `hdf5` `1.10.6` and `1.10.7`.
5151
- Added support for creating external links on a `Group` with `link_external`.
52-
- Added `Location` methods: `get_info`, `get_info_by_name`, `loc_type`, and `open_by_token`.
52+
- Added `Location` methods returning `LocationInfo`: `loc_info`, `loc_info_by_name`.
53+
- Added `Location` methods returning `LocationType`: `loc_type`, `loc_type_by_name`.
54+
- Added `Location::open_by_token` that can open any object by its physical address.
5355
- Added `Group` methods: `iter_visit`, `iter_visit_default`, `get_all_of_type`, `datasets`, `groups`, and `named_datatypes`.
5456
- Added `Debug` for `Handle`.
5557
- Added method `try_borrow` on `Handle` for not taking ownership of an HDF5 object.
58+
- Added `Handle::id_type()` method.
59+
- Added `ObjectClass::cast()` method (safe counterpart to `cast_unchecked`).
60+
- Added downcast methods to `Object`: `as_file`, `as_group`, `as_datatype`, `as_dataspace`,
61+
`as_dataset`, `as_attr`, `as_location`, `as_container`, `as_plist`.
62+
- Simplify setting min library version in `FileAccess` plist: `libver_earliest`, `libver_v18`,
63+
`libver_v110`, `libver_latest`; getting min library version can be done via `libver`.
5664

5765
### Changed
5866

@@ -83,12 +91,19 @@
8391
- Handles to `hdf5` identifiers are no longer tracked via a registry and is instead handled by stricter semantics of ownership.
8492
- The handle to a `File` will not close all objects in a `File` when dropped, but instead uses a weak file close degree. For the old behaviour see `FileCloseDegree::Strong`.
8593
- Globals no longer creates a `lazy_static` per global.
94+
- Unsafe `ObjectClass::cast()` has been renamed to `ObjectClass::cast_unchecked()`.
95+
- Bump `winreg` (Windows only) to 0.10, `pretty_assertions` (dev) to 1.0.
8696

8797
### Fixed
8898

8999
- A memory leak of handles has been identified and fixed.
90100
- A race condition on libary initialisation from different threads was identified and fixed.
91101

102+
### Removed
103+
104+
- Free-standing functions `get_id_type`, `is_valid_id`, `is_valid_user_id` have been removed,
105+
but they are all accessible via `Handle`.
106+
92107
## 0.7.1
93108

94109
### Added

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ cfg-if = "1.0"
3838

3939
[dev-dependencies]
4040
paste = "1.0"
41-
pretty_assertions = "0.7"
41+
pretty_assertions = "1.0"
4242
rand = { version = "0.8", features = ["small_rng"] }
4343
regex = "1.3"
4444
scopeguard = "1.0"

hdf5-sys/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pkg-config = "0.3"
3838
[target.'cfg(windows)'.build-dependencies]
3939
serde = "1.0"
4040
serde_derive = "1.0"
41-
winreg = { version = "0.8", features = ["serialization-serde"]}
41+
winreg = { version = "0.10", features = ["serialization-serde"]}
4242

4343
[package.metadata.docs.rs]
4444
features = ["static", "zlib"]

src/class.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ pub trait ObjectClass: Sized {
2424

2525
fn from_id(id: hid_t) -> Result<Self> {
2626
h5lock!({
27-
if Self::is_valid_id_type(get_id_type(id)) {
28-
let handle = Handle::try_new(id)?;
27+
let handle = Handle::try_new(id)?;
28+
if Self::is_valid_id_type(handle.id_type()) {
2929
let obj = Self::from_handle(handle);
3030
obj.validate().map(|_| obj)
3131
} else {
@@ -50,17 +50,27 @@ pub trait ObjectClass: Sized {
5050
&mut *(self as *mut Self).cast::<T>()
5151
}
5252

53-
unsafe fn cast<T: ObjectClass>(self) -> T {
53+
unsafe fn cast_unchecked<T: ObjectClass>(self) -> T {
5454
// This method requires you to be 18 years or older to use it
55+
// (note: if it wasn't a trait method, it could be marked as const)
5556
let obj = ptr::read((&self as *const Self).cast());
5657
mem::forget(self);
5758
obj
5859
}
5960

61+
fn cast<T: ObjectClass>(self) -> Result<T> {
62+
let id_type = self.handle().id_type();
63+
if Self::is_valid_id_type(id_type) {
64+
Ok(unsafe { self.cast_unchecked() })
65+
} else {
66+
Err(format!("unable to cast {} ({:?}) into {}", Self::NAME, id_type, T::NAME).into())
67+
}
68+
}
69+
6070
fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6171
// TODO: this can moved out if/when specialization lands in stable
6272
h5lock!({
63-
if !is_valid_user_id(self.handle().id()) {
73+
if !self.handle().is_valid_user_id() {
6474
write!(f, "<HDF5 {}: invalid id>", Self::NAME)
6575
} else if let Some(d) = self.short_repr() {
6676
write!(f, "<HDF5 {}: {}>", Self::NAME, d)

src/handle.rs

Lines changed: 34 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,9 @@
1+
use std::mem;
2+
13
use hdf5_sys::h5i::{H5I_type_t, H5Idec_ref, H5Iget_ref, H5Iget_type, H5Iinc_ref, H5Iis_valid};
24

35
use crate::internal_prelude::*;
46

5-
pub fn get_id_type(id: hid_t) -> H5I_type_t {
6-
h5lock!({
7-
let tp = h5lock!(H5Iget_type(id));
8-
let valid = id > 0 && tp > H5I_BADID && tp < H5I_NTYPES;
9-
if valid {
10-
tp
11-
} else {
12-
H5I_BADID
13-
}
14-
})
15-
}
16-
17-
pub(crate) fn refcount(id: hid_t) -> Result<hsize_t> {
18-
h5call!(H5Iget_ref(id)).map(|x| x as _)
19-
}
20-
21-
pub fn is_valid_id(id: hid_t) -> bool {
22-
h5lock!({
23-
let tp = get_id_type(id);
24-
tp > H5I_BADID && tp < H5I_NTYPES
25-
})
26-
}
27-
28-
pub fn is_valid_user_id(id: hid_t) -> bool {
29-
h5lock!({ H5Iis_valid(id) == 1 })
30-
}
31-
327
/// A handle to an HDF5 object
338
#[derive(Debug)]
349
pub struct Handle {
@@ -38,25 +13,23 @@ pub struct Handle {
3813
impl Handle {
3914
/// Create a handle from object ID, taking ownership of it
4015
pub fn try_new(id: hid_t) -> Result<Self> {
41-
h5lock!({
42-
if is_valid_user_id(id) {
43-
Ok(Self { id })
44-
} else {
45-
Err(From::from(format!("Invalid handle id: {}", id)))
46-
}
47-
})
16+
let handle = Self { id };
17+
if handle.is_valid_user_id() {
18+
Ok(handle)
19+
} else {
20+
// Drop on an invalid handle could cause closing an unrelated object
21+
// in the destructor, hence it's important to prevent the drop here.
22+
mem::forget(handle);
23+
Err(From::from(format!("Invalid handle id: {}", id)))
24+
}
4825
}
4926

5027
/// Create a handle from object ID by cloning it
5128
pub fn try_borrow(id: hid_t) -> Result<Self> {
52-
h5lock!({
53-
if is_valid_user_id(id) {
54-
h5call!(H5Iinc_ref(id))?;
55-
Ok(Self { id })
56-
} else {
57-
Err(From::from(format!("Invalid handle id: {}", id)))
58-
}
59-
})
29+
// It's ok to just call try_new() since it may not decref the object
30+
let handle = Self::try_new(id)?;
31+
handle.incref();
32+
Ok(handle)
6033
}
6134

6235
pub const fn invalid() -> Self {
@@ -69,8 +42,8 @@ impl Handle {
6942

7043
/// Increment the reference count of the handle
7144
pub fn incref(&self) {
72-
if is_valid_user_id(self.id()) {
73-
h5lock!(H5Iinc_ref(self.id()));
45+
if self.is_valid_user_id() {
46+
h5lock!(H5Iinc_ref(self.id));
7447
}
7548
}
7649

@@ -81,30 +54,42 @@ impl Handle {
8154
pub fn decref(&self) {
8255
h5lock!({
8356
if self.is_valid_id() {
84-
H5Idec_ref(self.id());
57+
H5Idec_ref(self.id);
8558
}
8659
});
8760
}
8861

8962
/// Returns `true` if the object has a valid unlocked identifier (`false` for pre-defined
9063
/// locked identifiers like property list classes).
9164
pub fn is_valid_user_id(&self) -> bool {
92-
is_valid_user_id(self.id())
65+
h5lock!(H5Iis_valid(self.id)) == 1
9366
}
9467

9568
pub fn is_valid_id(&self) -> bool {
96-
is_valid_id(self.id())
69+
matches!(self.id_type(), tp if tp > H5I_BADID && tp < H5I_NTYPES)
9770
}
9871

9972
/// Return the reference count of the object
10073
pub fn refcount(&self) -> u32 {
101-
refcount(self.id).unwrap_or(0) as _
74+
h5call!(H5Iget_ref(self.id)).map(|x| x as _).unwrap_or(0) as _
75+
}
76+
77+
/// Get HDF5 object type as a native enum.
78+
pub fn id_type(&self) -> H5I_type_t {
79+
if self.id <= 0 {
80+
H5I_BADID
81+
} else {
82+
match h5lock!(H5Iget_type(self.id)) {
83+
tp if tp > H5I_BADID && tp < H5I_NTYPES => tp,
84+
_ => H5I_BADID,
85+
}
86+
}
10287
}
10388
}
10489

10590
impl Clone for Handle {
10691
fn clone(&self) -> Self {
107-
Self::try_borrow(self.id()).unwrap_or_else(|_| Self::invalid())
92+
Self::try_borrow(self.id).unwrap_or_else(|_| Self::invalid())
10893
}
10994
}
11095

src/hl/attribute.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,6 @@ pub mod attribute_tests {
397397
pub fn test_missing() {
398398
with_tmp_file(|file| {
399399
let _ = file.new_attr::<u32>().shape((1, 2)).create("foo").unwrap();
400-
401400
let missing_result = file.attr("bar");
402401
assert!(missing_result.is_err());
403402
})
@@ -407,22 +406,11 @@ pub mod attribute_tests {
407406
pub fn test_write_read_str() {
408407
with_tmp_file(|file| {
409408
let s = VarLenUnicode::from_str("var len foo").unwrap();
410-
411-
println!("file.new_attribute");
412409
let attr = file.new_attr::<VarLenUnicode>().shape(()).create("foo").unwrap();
413-
414-
println!("write attribute");
415410
attr.as_writer().write_scalar(&s).unwrap();
416-
417-
println!("get attribute");
418411
let read_attr = file.attr("foo").unwrap();
419-
420-
println!("attribute shape");
421412
assert_eq!(read_attr.shape(), []);
422-
423-
println!("read attribute");
424413
let r: VarLenUnicode = read_attr.as_reader().read_scalar().unwrap();
425-
426414
assert_eq!(r, s);
427415
})
428416
}
@@ -433,9 +421,7 @@ pub mod attribute_tests {
433421
let arr1 = arr2(&[[123], [456]]);
434422
let _attr1 = file.new_attr_builder().with_data(&arr1).create("foo").unwrap();
435423
let _attr2 = file.new_attr_builder().with_data("string").create("bar").unwrap();
436-
437424
let attr_names = file.attr_names().unwrap();
438-
439425
assert_eq!(attr_names.len(), 2);
440426
assert!(attr_names.contains(&"foo".to_string()));
441427
assert!(attr_names.contains(&"bar".to_string()));

src/hl/container.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ impl Deref for Container {
380380

381381
impl Container {
382382
pub(crate) fn is_attr(&self) -> bool {
383-
get_id_type(self.id()) == H5I_ATTR
383+
self.handle().id_type() == H5I_ATTR
384384
}
385385

386386
/// Creates a reader wrapper for this dataset/attribute, allowing to

src/hl/dataspace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ mod tests {
208208

209209
#[test]
210210
fn test_dataspace_err() {
211-
assert_err!(Dataspace::from_id(H5I_INVALID_HID), "Invalid dataspace id");
211+
assert_err!(Dataspace::from_id(H5I_INVALID_HID), "Invalid handle id");
212212
}
213213

214214
#[test]

src/hl/file.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,10 @@ pub mod tests {
485485
})
486486
}
487487

488+
fn rc(id: hid_t) -> Result<hsize_t> {
489+
h5call!(hdf5_sys::h5i::H5Iget_ref(id)).map(|x| x as _)
490+
}
491+
488492
#[test]
489493
fn test_strong_close() {
490494
use crate::hl::plist::file_access::FileCloseDegree;
@@ -506,16 +510,16 @@ pub mod tests {
506510
assert_eq!(file_copy.refcount(), 2);
507511

508512
drop(file);
509-
assert_eq!(crate::handle::refcount(fileid).unwrap(), 1);
513+
assert_eq!(rc(fileid).unwrap(), 1);
510514
assert_eq!(group.refcount(), 1);
511515
assert_eq!(file_copy.refcount(), 1);
512516

513517
h5lock!({
514518
// Lock to ensure fileid does not get overwritten
515519
let groupid = group.id();
516520
drop(file_copy);
517-
assert!(crate::handle::refcount(fileid).is_err());
518-
assert!(crate::handle::refcount(groupid).is_err());
521+
assert!(rc(fileid).is_err());
522+
assert!(rc(groupid).is_err());
519523
assert!(!group.is_valid());
520524
drop(group);
521525
});
@@ -543,14 +547,14 @@ pub mod tests {
543547
assert_eq!(file_copy.refcount(), 2);
544548

545549
drop(file);
546-
assert_eq!(crate::handle::refcount(fileid).unwrap(), 1);
550+
assert_eq!(rc(fileid).unwrap(), 1);
547551
assert_eq!(group.refcount(), 1);
548552
assert_eq!(file_copy.refcount(), 1);
549553

550554
h5lock!({
551555
// Lock to ensure fileid does not get overwritten
552556
drop(file_copy);
553-
assert!(crate::handle::refcount(fileid).is_err());
557+
assert!(rc(fileid).is_err());
554558
});
555559
assert_eq!(group.refcount(), 1);
556560
});

src/hl/group.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ impl Group {
362362

363363
fn get_all_of_type(&self, loc_type: LocationType) -> Result<Vec<Location>> {
364364
self.iter_visit_default(vec![], |group, name, _info, objects| {
365-
if let Ok(info) = group.get_info_by_name(name) {
365+
if let Ok(info) = group.loc_info_by_name(name) {
366366
if info.loc_type == loc_type {
367367
if let Ok(loc) = group.open_by_token(info.token) {
368368
objects.push(loc);
@@ -379,19 +379,19 @@ impl Group {
379379
/// Returns all groups in the group, non-recursively
380380
pub fn groups(&self) -> Result<Vec<Self>> {
381381
self.get_all_of_type(LocationType::Group)
382-
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast() }).collect())
382+
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast_unchecked() }).collect())
383383
}
384384

385385
/// Returns all datasets in the group, non-recursively
386386
pub fn datasets(&self) -> Result<Vec<Dataset>> {
387387
self.get_all_of_type(LocationType::Dataset)
388-
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast() }).collect())
388+
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast_unchecked() }).collect())
389389
}
390390

391391
/// Returns all named types in the group, non-recursively
392392
pub fn named_datatypes(&self) -> Result<Vec<Datatype>> {
393393
self.get_all_of_type(LocationType::NamedDatatype)
394-
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast() }).collect())
394+
.map(|vec| vec.into_iter().map(|obj| unsafe { obj.cast_unchecked() }).collect())
395395
}
396396

397397
/// Returns the names of all objects in the group, non-recursively.

0 commit comments

Comments
 (0)