diff --git a/hdf5-src/ext/hdf5 b/hdf5-src/ext/hdf5 index 0fe0459fc..db30c2da6 160000 --- a/hdf5-src/ext/hdf5 +++ b/hdf5-src/ext/hdf5 @@ -1 +1 @@ -Subproject commit 0fe0459fc24d71be13d5f266513c2832b525671b +Subproject commit db30c2da68ece4a155e9e50c28ec16d6057509b2 diff --git a/hdf5-types/build.rs b/hdf5-types/build.rs index 22c3ba138..4289d8bbe 100644 --- a/hdf5-types/build.rs +++ b/hdf5-types/build.rs @@ -4,4 +4,15 @@ fn main() { if std::env::var_os("DEP_HDF5_MSVC_DLL_INDIRECTION").is_some() { println!("cargo::rustc-cfg=windows_dll"); } + + // Declare the known HDF5 versions we might feature flag on + // in this crate. + println!("cargo::rustc-check-cfg=cfg(feature, values(\"1.12.0\"))"); + + for (key, _) in std::env::vars() { + if key.starts_with("DEP_HDF5_VERSION_") { + let version = key.trim_start_matches("DEP_HDF5_VERSION_").replace("_", "."); + println!("cargo::rustc-cfg=feature=\"{version}\""); + } + } } diff --git a/hdf5-types/src/dyn_value.rs b/hdf5-types/src/dyn_value.rs index 1ef50ef33..23a09357a 100644 --- a/hdf5-types/src/dyn_value.rs +++ b/hdf5-types/src/dyn_value.rs @@ -703,6 +703,7 @@ impl<'a> DynValue<'a> { FixedUnicode(_) => DynFixedString::new(buf, true).into(), VarLenAscii => DynVarLenString::new(buf, false).into(), VarLenUnicode => DynVarLenString::new(buf, true).into(), + Reference(_x) => todo!(), } } } diff --git a/hdf5-types/src/h5type.rs b/hdf5-types/src/h5type.rs index f8dbf6dd6..251be5b21 100644 --- a/hdf5-types/src/h5type.rs +++ b/hdf5-types/src/h5type.rs @@ -4,6 +4,7 @@ use std::os::raw::c_void; use std::ptr; use crate::array::VarLenArray; +use crate::references::Reference; use crate::string::{FixedAscii, FixedUnicode, VarLenAscii, VarLenUnicode}; #[allow(non_camel_case_types)] @@ -160,6 +161,7 @@ pub enum TypeDescriptor { VarLenArray(Box), VarLenAscii, VarLenUnicode, + Reference(Reference), } impl Display for TypeDescriptor { @@ -186,6 +188,10 @@ impl Display for TypeDescriptor { TypeDescriptor::VarLenArray(ref tp) => write!(f, "[{}] (var len)", tp), TypeDescriptor::VarLenAscii => write!(f, "string (var len)"), TypeDescriptor::VarLenUnicode => write!(f, "unicode (var len)"), + TypeDescriptor::Reference(Reference::Object) => write!(f, "reference (object)"), + TypeDescriptor::Reference(Reference::Region) => write!(f, "reference (region)"), + #[cfg(feature = "1.12.0")] + TypeDescriptor::Reference(Reference::Std) => write!(f, "reference"), } } } @@ -202,6 +208,7 @@ impl TypeDescriptor { Self::FixedAscii(len) | Self::FixedUnicode(len) => len, Self::VarLenArray(_) => mem::size_of::(), Self::VarLenAscii | Self::VarLenUnicode => mem::size_of::<*const u8>(), + Self::Reference(reftyp) => reftyp.size(), } } @@ -340,7 +347,7 @@ unsafe impl H5Type for [T; N] { } } -unsafe impl H5Type for VarLenArray { +unsafe impl H5Type for VarLenArray { #[inline] fn type_descriptor() -> TypeDescriptor { TypeDescriptor::VarLenArray(Box::new(::type_descriptor())) diff --git a/hdf5-types/src/lib.rs b/hdf5-types/src/lib.rs index c5de9ac15..a1daa8337 100644 --- a/hdf5-types/src/lib.rs +++ b/hdf5-types/src/lib.rs @@ -20,6 +20,7 @@ extern crate quickcheck; mod array; pub mod dyn_value; mod h5type; +pub mod references; mod string; #[cfg(feature = "complex")] @@ -30,6 +31,7 @@ pub use self::dyn_value::{DynValue, OwnedDynValue}; pub use self::h5type::{ CompoundField, CompoundType, EnumMember, EnumType, FloatSize, H5Type, IntSize, TypeDescriptor, }; +pub use self::references::Reference; pub use self::string::{FixedAscii, FixedUnicode, StringError, VarLenAscii, VarLenUnicode}; pub(crate) unsafe fn malloc(n: usize) -> *mut core::ffi::c_void { diff --git a/hdf5-types/src/references.rs b/hdf5-types/src/references.rs new file mode 100644 index 000000000..a9649958f --- /dev/null +++ b/hdf5-types/src/references.rs @@ -0,0 +1,22 @@ +//! Types for references. + +use std::mem; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Reference { + Object, + Region, + #[cfg(feature = "1.12.0")] + Std, +} + +impl Reference { + pub fn size(self) -> usize { + match self { + Self::Object => mem::size_of::(), + Self::Region => mem::size_of::(), + #[cfg(feature = "1.12.0")] + Self::Std => mem::size_of::(), + } + } +} diff --git a/hdf5/src/globals.rs b/hdf5/src/globals.rs index 16f174214..07ac09a57 100644 --- a/hdf5/src/globals.rs +++ b/hdf5/src/globals.rs @@ -77,6 +77,8 @@ link_hid!(H5T_STD_B64BE, h5t::H5T_STD_B64BE); link_hid!(H5T_STD_B64LE, h5t::H5T_STD_B64LE); link_hid!(H5T_STD_REF_OBJ, h5t::H5T_STD_REF_OBJ); link_hid!(H5T_STD_REF_DSETREG, h5t::H5T_STD_REF_DSETREG); +#[cfg(feature = "1.12.0")] +link_hid!(H5T_STD_REF, h5t::H5T_STD_REF); link_hid!(H5T_UNIX_D32BE, h5t::H5T_UNIX_D32BE); link_hid!(H5T_UNIX_D32LE, h5t::H5T_UNIX_D32LE); link_hid!(H5T_UNIX_D64BE, h5t::H5T_UNIX_D64BE); diff --git a/hdf5/src/hl.rs b/hdf5/src/hl.rs index ac813036f..5cae2f0b9 100644 --- a/hdf5/src/hl.rs +++ b/hdf5/src/hl.rs @@ -11,6 +11,7 @@ pub mod group; pub mod location; pub mod object; pub mod plist; +pub mod references; pub mod selection; pub use self::{ diff --git a/hdf5/src/hl/datatype.rs b/hdf5/src/hl/datatype.rs index 9c9ebe58a..1226e8126 100644 --- a/hdf5/src/hl/datatype.rs +++ b/hdf5/src/hl/datatype.rs @@ -410,6 +410,16 @@ impl Datatype { } TD::VarLenAscii => string_type(None, H5T_cset_t::H5T_CSET_ASCII), TD::VarLenUnicode => string_type(None, H5T_cset_t::H5T_CSET_UTF8), + #[cfg(feature = "1.12.0")] + TD::Reference(hdf5_types::Reference::Std) => { + Ok(h5try!(H5Tcopy(*crate::globals::H5T_STD_REF))) + } + TD::Reference(hdf5_types::Reference::Object) => { + Ok(h5try!(H5Tcopy(*crate::globals::H5T_STD_REF_OBJ))) + } + TD::Reference(hdf5_types::Reference::Region) => { + Ok(h5try!(H5Tcopy(*crate::globals::H5T_STD_REF_DSETREG))) + } } }); diff --git a/hdf5/src/hl/location.rs b/hdf5/src/hl/location.rs index a1b9fda20..bc4fff684 100644 --- a/hdf5/src/hl/location.rs +++ b/hdf5/src/hl/location.rs @@ -146,6 +146,20 @@ impl Location { pub fn open_by_token(&self, token: LocationToken) -> Result { H5O_open_by_token(self.id(), token) } + + /// Generate a [object reference](ObjectReference) to the object for a reference storage. + /// + /// This can be a group, dataset or datatype. Other objects are not supported. + pub fn reference(&self, name: &str) -> Result { + R::create(self, name) + } + + /// Get a reference back to the referenced object from a standard reference. + /// + /// This can be called against any object in the same file as the referenced object. + pub fn dereference(&self, reference: &R) -> Result { + reference.dereference(self) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/hdf5/src/hl/references.rs b/hdf5/src/hl/references.rs new file mode 100644 index 000000000..1fcece4de --- /dev/null +++ b/hdf5/src/hl/references.rs @@ -0,0 +1,61 @@ +use crate::internal_prelude::*; +use crate::Location; + +mod legacy; + +#[cfg(feature = "1.12.1")] +mod standard; + +use hdf5_sys::h5o::H5O_type_t; +use hdf5_sys::h5r::H5R_type_t; + +pub use legacy::ObjectReference1; +#[cfg(feature = "1.12.1")] +pub use standard::ObjectReference2; + +mod private { + pub trait ObjectReferencePrivate {} +} + +/// The trait for all object references. This provides a common interface +/// over the legacy and standard reference types. +/// +/// This trait is sealed and cannot be implemented for types outside `hdf5::hl`. +pub trait ObjectReference: Sized + H5Type + private::ObjectReferencePrivate { + const REF_TYPE: H5R_type_t; + fn ptr(&self) -> *const c_void; + + /// Get the type of the object that the reference points in the same space as the provided location. + fn get_object_type(&self, location: &Location) -> Result; + + /// Create a new reference in the same structure as the location provided. + fn create(location: &Location, name: &str) -> Result; + + /// Dereference the object reference in the space provided. + fn dereference(&self, location: &Location) -> Result; +} +/// The result of dereferencing an [object reference](ObjectReference). +/// +/// Each variant represents a different type of object that can be referenced by a [ObjectReference]. +#[derive(Clone, Debug)] +pub enum ReferencedObject { + Group(Group), + Dataset(Dataset), + Datatype(Datatype), +} + +impl ReferencedObject { + pub fn from_type_and_id(object_type: H5O_type_t, object_id: hid_t) -> Result { + use hdf5_sys::h5o::H5O_type_t::*; + let referenced_object = match object_type { + H5O_TYPE_GROUP => ReferencedObject::Group(Group::from_id(object_id)?), + H5O_TYPE_DATASET => ReferencedObject::Dataset(Dataset::from_id(object_id)?), + H5O_TYPE_NAMED_DATATYPE => ReferencedObject::Datatype(Datatype::from_id(object_id)?), + #[cfg(feature = "1.12.0")] + H5O_TYPE_MAP => fail!("Can not create object from a map"), + H5O_TYPE_UNKNOWN => fail!("Unknown datatype"), + H5O_TYPE_NTYPES => fail!("hdf5 should not produce this type"), + }; + Ok(referenced_object) + } +} diff --git a/hdf5/src/hl/references/legacy.rs b/hdf5/src/hl/references/legacy.rs new file mode 100644 index 000000000..d60ae0c6c --- /dev/null +++ b/hdf5/src/hl/references/legacy.rs @@ -0,0 +1,72 @@ +//! The pre v1.12.0 reference types. + +use crate::internal_prelude::*; + +use hdf5_sys::{ + h5o::H5O_type_t, + h5p::H5P_DEFAULT, + h5r::{hobj_ref_t, H5Rcreate, H5Rdereference, H5Rget_obj_type2}, +}; +use hdf5_types::H5Type; + +#[cfg(not(feature = "1.12.0"))] +use hdf5_sys::h5r::H5R_OBJECT as H5R_OBJECT1; +#[cfg(feature = "1.12.0")] +use hdf5_sys::h5r::H5R_OBJECT1; + +use super::{private::ObjectReferencePrivate, ObjectReference}; +use crate::Location; + +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct ObjectReference1 { + inner: hobj_ref_t, +} + +unsafe impl H5Type for ObjectReference1 { + fn type_descriptor() -> hdf5_types::TypeDescriptor { + hdf5_types::TypeDescriptor::Reference(hdf5_types::Reference::Object) + } +} + +impl ObjectReferencePrivate for ObjectReference1 {} + +impl ObjectReference for ObjectReference1 { + const REF_TYPE: hdf5_sys::h5r::H5R_type_t = H5R_OBJECT1; + + fn ptr(&self) -> *const c_void { + let pointer = std::ptr::addr_of!(self.inner); + pointer.cast() + } + + fn create(location: &Location, name: &str) -> Result { + let mut ref_out: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); + let name = to_cstring(name)?; + h5call!(H5Rcreate( + ref_out.as_mut_ptr().cast(), + location.id(), + name.as_ptr(), + Self::REF_TYPE, + -1 + ))?; + let reference = unsafe { ref_out.assume_init() }; + Ok(Self { inner: reference }) + } + + fn get_object_type(&self, location: &Location) -> Result { + let mut objtype = std::mem::MaybeUninit::::uninit(); + h5call!(H5Rget_obj_type2(location.id(), H5R_OBJECT1, self.ptr(), objtype.as_mut_ptr()))?; + let objtype = unsafe { objtype.assume_init() }; + Ok(objtype) + } + + fn dereference(&self, location: &Location) -> Result { + let object_type = self.get_object_type(location)?; + #[cfg(feature = "1.10.0")] + let object_id = + h5call!(H5Rdereference(location.id(), H5P_DEFAULT, H5R_OBJECT1, self.ptr()))?; + #[cfg(not(feature = "1.10.0"))] + let object_id = h5call!(H5Rdereference(location.id(), H5R_OBJECT1, self.ptr()))?; + ReferencedObject::from_type_and_id(object_type, object_id) + } +} diff --git a/hdf5/src/hl/references/standard.rs b/hdf5/src/hl/references/standard.rs new file mode 100644 index 000000000..edd35682b --- /dev/null +++ b/hdf5/src/hl/references/standard.rs @@ -0,0 +1,106 @@ +//! New standard reference types introduced in v1.12.0. +//! +//! These are gated on v1.12.1 since there appear to be multiple bugs in v1.12.0. +//! +use hdf5_sys::h5o::H5O_type_t; +use hdf5_sys::h5r::H5R_type_t::H5R_OBJECT2; +use hdf5_sys::h5r::{H5R_ref_t, H5Rcreate_object, H5Rdestroy, H5Rget_obj_type3, H5Ropen_object}; + +use super::{private::ObjectReferencePrivate, ObjectReference}; +use crate::internal_prelude::*; +use crate::Location; + +/// A reference to a HDF5 item that can be stored in attributes or datasets. +#[repr(transparent)] +pub struct StdReference(H5R_ref_t); + +impl StdReference { + fn ptr(&self) -> *const H5R_ref_t { + std::ptr::addr_of!(self.0) + } +} + +//todo: could we query some actual object parameters to make this more useful? +impl std::fmt::Debug for StdReference { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("StdReference") + } +} + +unsafe impl H5Type for StdReference { + fn type_descriptor() -> hdf5_types::TypeDescriptor { + hdf5_types::TypeDescriptor::Reference(hdf5_types::Reference::Std) + } +} + +impl Drop for StdReference { + fn drop(&mut self) { + let _e = h5call!(H5Rdestroy(&mut self.0)); + } +} + +#[repr(transparent)] +#[derive(Debug)] +pub struct ObjectReference2(StdReference); + +impl ObjectReferencePrivate for ObjectReference2 {} + +impl ObjectReference for ObjectReference2 { + const REF_TYPE: hdf5_sys::h5r::H5R_type_t = H5R_OBJECT2; + + fn ptr(&self) -> *const c_void { + self.0.ptr().cast() + } + + fn create(location: &Location, name: &str) -> Result { + let reference: H5R_ref_t = create_object_reference(location, name)?; + Ok(Self(StdReference(reference))) + } + + fn get_object_type(&self, _location: &Location) -> Result { + let mut objtype = std::mem::MaybeUninit::::uninit(); + h5call!(H5Rget_obj_type3(self.0.ptr(), H5P_DEFAULT, objtype.as_mut_ptr()))?; + let objtype = unsafe { objtype.assume_init() }; + Ok(objtype) + } + + fn dereference(&self, location: &Location) -> Result { + let object_type = self.get_object_type(location)?; + let object_id = h5call!(H5Ropen_object(self.0.ptr(), H5P_DEFAULT, H5P_DEFAULT))?; + ReferencedObject::from_type_and_id(object_type, object_id) + } +} + +unsafe impl H5Type for ObjectReference2 { + fn type_descriptor() -> hdf5_types::TypeDescriptor { + hdf5_types::TypeDescriptor::Reference(hdf5_types::Reference::Std) + } +} + +fn create_object_reference(dataset: &Location, name: &str) -> Result { + let mut out: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); + let name = to_cstring(name)?; + h5call!(H5Rcreate_object(dataset.id(), name.as_ptr(), H5P_DEFAULT, out.as_mut_ptr().cast(),))?; + unsafe { Ok(out.assume_init()) } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_references() { + use super::ReferencedObject; + with_tmp_file(|file| { + file.create_group("g").unwrap(); + let gref = file.reference::("g").unwrap(); + let group = file.dereference(&gref).unwrap(); + assert!(matches!(group, ReferencedObject::Group(_))); + + file.new_dataset::().create("ds").unwrap(); + let dsref = file.reference::("ds").unwrap(); + let ds = file.dereference(&dsref).unwrap(); + assert!(matches!(ds, ReferencedObject::Dataset(_))); + }) + } +} diff --git a/hdf5/src/lib.rs b/hdf5/src/lib.rs index c8d704266..12a1192b5 100644 --- a/hdf5/src/lib.rs +++ b/hdf5/src/lib.rs @@ -58,6 +58,7 @@ mod export { hl::extents::{Extent, Extents, SimpleExtents}, hl::selection::{Hyperslab, Selection, SliceOrIndex}, hl::{ + references::{ObjectReference, ObjectReference1, ReferencedObject}, Attribute, AttributeBuilder, AttributeBuilderData, AttributeBuilderEmpty, AttributeBuilderEmptyShape, ByteReader, Container, Conversion, Dataset, DatasetBuilder, DatasetBuilderData, DatasetBuilderEmpty, DatasetBuilderEmptyShape, Dataspace, Datatype, @@ -66,6 +67,9 @@ mod export { }, }; + #[cfg(feature = "1.12.1")] + pub use crate::hl::references::ObjectReference2; + #[doc(hidden)] pub use crate::error::h5check; diff --git a/hdf5/tests/test_object_references.rs b/hdf5/tests/test_object_references.rs new file mode 100644 index 000000000..ad1888d5a --- /dev/null +++ b/hdf5/tests/test_object_references.rs @@ -0,0 +1,248 @@ +//! Tests for the reference type storage and retrieval. +//! + +mod common; + +use common::util::new_in_memory_file; +#[cfg(feature = "1.12.1")] +use hdf5::ObjectReference2; +use hdf5::{H5Type, ObjectReference, ObjectReference1, ReferencedObject}; +use hdf5_metno as hdf5; + +fn test_group_references() { + let file = new_in_memory_file().unwrap(); + let g1 = file.create_group("g1").unwrap(); + let _g1_1 = g1.create_group("g1_1").unwrap(); + + let refs: [R; 2] = [file.reference("g1").unwrap(), g1.reference("g1_1").unwrap()]; + + let ds = file.new_dataset_builder().with_data(&refs).create("refs").unwrap(); + + let read_references = ds.read_1d::().unwrap(); + + match file.dereference(&read_references[0]).unwrap() { + ReferencedObject::Group(g) => { + assert_eq!(g.name(), "/g1"); + } + _ => { + panic!("Expected a group reference"); + } + } + + match file.dereference(&read_references[1]).unwrap() { + ReferencedObject::Group(g) => { + assert_eq!(g.name(), "/g1/g1_1"); + } + _ => { + panic!("Expected a group reference"); + } + } + + match g1.dereference(&read_references[1]).expect("Dereference against the group.") { + ReferencedObject::Group(g) => { + assert_eq!(g.name(), "/g1/g1_1"); + } + _ => { + panic!("Expected a group reference"); + } + } +} + +fn test_dataset_references() { + let dummy_data = [0, 1, 2, 3]; + + let file = new_in_memory_file().unwrap(); + let _ds1 = file.new_dataset_builder().with_data(&dummy_data).create("ds1").unwrap(); + let g = file.create_group("g").unwrap(); + let _ds2 = g.new_dataset_builder().with_data(&dummy_data).create("ds2").unwrap(); + let refs: [R; 2] = [file.reference("ds1").unwrap(), g.reference("ds2").unwrap()]; + + let ds_refs = file.new_dataset_builder().with_data(&refs).create("refs").unwrap(); + let read_references = ds_refs.read_1d::().unwrap(); + + match file.dereference(&read_references[0]).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds1"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &dummy_data); + } + _ => { + panic!("Expected a dataset reference"); + } + } + + match file.dereference(&read_references[1]).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/g/ds2"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &dummy_data); + } + _ => { + panic!("Expected a dataset reference"); + } + } +} + +fn test_reference_in_attribute() { + let file = new_in_memory_file().unwrap(); + let _ds1 = file.new_dataset_builder().with_data(&[1, 2, 3]).create("ds1").unwrap(); + let ref1: R = file.reference("ds1").unwrap(); + + file.new_attr::().create("ref_attr").unwrap().write_scalar(&ref1).unwrap(); + + let ref_read = file.attr("ref_attr").unwrap().read_scalar::().unwrap(); + + match file.dereference(&ref_read).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds1"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &[1, 2, 3]); + } + _ => { + panic!("Expected a dataset reference"); + } + } +} + +fn test_reference_errors_on_attribute() { + let file = new_in_memory_file().unwrap(); + let _attr = file.new_attr::().create("ref_attr").unwrap(); + // Attempt to create reference to attribute should fail. + let result = file.reference::("ref_attr"); + assert!(result.is_err()); +} + +fn test_reference_in_datatype() { + let dummy_data = [1, 2, 3, 4]; + let file = new_in_memory_file().unwrap(); + let _ds1 = file.new_dataset_builder().with_data(&dummy_data).create("ds1").unwrap(); + let ref1 = file.reference::("ds1").unwrap(); + let _ds2 = file.new_dataset_builder().with_data(&dummy_data).create("ds2").unwrap(); + let ref2 = file.reference::("ds2").unwrap(); + + #[derive(H5Type)] + #[repr(C)] + struct RefData { + dataset: R, + value: i32, + } + + let ds3 = file + .new_dataset_builder() + .with_data(&[RefData { dataset: ref1, value: 42 }, RefData { dataset: ref2, value: 43 }]) + .create("ds3") + .unwrap(); + + let read_data = ds3.read_1d::>().unwrap(); + assert_eq!(read_data[0].value, 42); + assert_eq!(read_data[1].value, 43); + match file.dereference(&read_data[0].dataset).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds1"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &dummy_data); + } + _ => { + panic!("Expected a dataset reference"); + } + } + match file.dereference(&read_data[1].dataset).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds2"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &dummy_data); + } + _ => { + panic!("Expected a dataset reference"); + } + } +} + +/* TODO: Should this be possible? Reference not implementing Copy blocks this in a few places. +#[test] +fn test_references_in_array_types() { + let file = new_in_memory_file().unwrap(); + let _ds1 = file.new_dataset_builder().with_data(&[1, 2, 3]).create("ds1").unwrap(); + let _ds2 = file.new_dataset_builder().with_data(&[4, 5, 6]).create("ds2").unwrap(); + let refs = [file.reference("ds1").unwrap(), file.reference("ds2").unwrap()]; + let refs_array = VarLenArray::from_slice(&refs); + + file.new_attr::>() + .create("var_array") + .unwrap() + .write_scalar(&refs) + .unwrap(); + + let read_array = + file.attr("var_array").unwrap().read_scalar::>().unwrap(); + + let read_refs = read_array.as_slice(); + + assert_eq!(read_refs.len(), 2); + match file.dereference(&read_refs[0]).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds1"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &[1, 2, 3]); + } + _ => { + panic!("Expected a dataset reference"); + } + } + match file.dereference(&read_refs[1]).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds2"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &[4, 5, 6]); + } + _ => { + panic!("Expected a dataset reference"); + } + } +} +*/ +#[test] +fn test_group_references_with_objectreference1() { + test_group_references::(); +} + +#[test] +fn test_dataset_references_with_object_reference1() { + test_dataset_references::(); +} +#[test] +fn test_reference_in_attribute_object_reference1() { + test_reference_in_attribute::(); +} + +#[test] +fn test_reference_errors_on_attribute_object_reference1() { + test_reference_errors_on_attribute::(); +} + +#[test] +fn test_reference_in_datatype_object_reference1() { + test_reference_in_datatype::(); +} + +#[cfg(feature = "1.12.1")] +#[test] +fn test_group_references_with_objectreference2() { + test_group_references::(); +} + +#[cfg(feature = "1.12.1")] +#[test] +fn test_dataset_references_with_object_reference2() { + test_dataset_references::(); +} +#[cfg(feature = "1.12.1")] +#[test] +fn test_reference_in_attribute_object_reference2() { + test_reference_in_attribute::(); +} + +#[cfg(feature = "1.12.1")] +#[test] +fn test_reference_errors_on_attribute_object_reference2() { + test_reference_errors_on_attribute::(); +} + +#[cfg(feature = "1.12.1")] +#[test] +fn test_reference_in_datatype_object_reference2() { + test_reference_in_datatype::(); +}