diff --git a/src/attribute.rs b/src/attribute.rs index 928d33f..2331a39 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -16,10 +16,6 @@ pub struct Attribute<'a> { pub(crate) ncid: nc_type, /// Variable/global this id is connected to pub(crate) varid: nc_type, - /// Attribute type - pub(crate) atttype: nc_type, - /// Vector length - pub(crate) attlen: nc_type, /// Holds the variable/group to prevent the /// attribute being deleted or modified pub(crate) _marker: PhantomData<&'a nc_type>, @@ -34,9 +30,7 @@ impl<'a> std::fmt::Debug for Attribute<'a> { write!(f, "<>")?; } write!(f, "ncid: {}", self.ncid)?; - write!(f, "varid: {}", self.varid)?; - write!(f, "atttype: {}", self.atttype)?; - write!(f, "attlen: {}", self.attlen) + write!(f, "varid: {}", self.varid) } } @@ -53,152 +47,305 @@ impl<'a> Attribute<'a> { .unwrap_or(self.name.len()); std::str::from_utf8(&self.name[..zeropos]) } - /// Get the value of the attribute - #[allow(clippy::too_many_lines)] - pub fn value(&self) -> error::Result { - let mut typ = 0; + fn num_elems(&self) -> error::Result { let _l = LOCK.lock().unwrap(); + let mut nelems = 0; + unsafe { + error::checked(nc_inq_attlen( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut nelems, + ))?; + } + Ok(nelems as _) + } + fn typ(&self) -> error::Result { + let _l = LOCK.lock().unwrap(); + let mut atttype = 0; unsafe { error::checked(nc_inq_atttype( self.ncid, self.varid, self.name.as_ptr() as *const _, - &mut typ, + &mut atttype, ))?; } + Ok(atttype) + } + /// Get the value of the attribute + #[allow(clippy::too_many_lines)] + pub fn value(&self) -> error::Result { + let attlen = self.num_elems()?; + let typ = self.typ()?; + let _l = LOCK.lock().unwrap(); match typ { - NC_UBYTE => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_uchar( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + NC_UBYTE => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_uchar( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Uchar(value)) } - Ok(AttrValue::Uchar(value)) - } - NC_BYTE => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_schar( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + len => { + let mut values = vec![0_u8; len as usize]; + unsafe { + error::checked(nc_get_att_uchar( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Uchars(values)) } - Ok(AttrValue::Schar(value)) - } - NC_SHORT => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_short( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + }, + NC_BYTE => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_schar( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Schar(value)) } - Ok(AttrValue::Short(value)) - } - NC_USHORT => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_ushort( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_schar( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Schars(values)) } - Ok(AttrValue::Ushort(value)) - } - NC_INT => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_int( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + }, + NC_SHORT => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_short( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Short(value)) } - Ok(AttrValue::Int(value)) - } - NC_UINT => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_uint( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_short( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Shorts(values)) } - Ok(AttrValue::Uint(value)) - } - NC_INT64 => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_longlong( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + }, + NC_USHORT => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_ushort( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Ushort(value)) } - Ok(AttrValue::Longlong(value)) - } - NC_UINT64 => { - let mut value = 0; - unsafe { - error::checked(nc_get_att_ulonglong( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_ushort( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Ushorts(values)) } - Ok(AttrValue::Ulonglong(value)) - } - NC_FLOAT => { - let mut value = 0.0; - unsafe { - error::checked(nc_get_att_float( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + }, + NC_INT => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_int( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Int(value)) } - Ok(AttrValue::Float(value)) - } - NC_DOUBLE => { - let mut value = 0.0; - unsafe { - error::checked(nc_get_att_double( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut value, - ))?; + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_int( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Ints(values)) } - Ok(AttrValue::Double(value)) - } + }, + NC_UINT => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_uint( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Uint(value)) + } + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_uint( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Uints(values)) + } + }, + NC_INT64 => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_longlong( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Longlong(value)) + } + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_longlong( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + + Ok(AttrValue::Longlongs(values)) + } + }, + NC_UINT64 => match attlen { + 1 => { + let mut value = 0; + unsafe { + error::checked(nc_get_att_ulonglong( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Ulonglong(value)) + } + len => { + let mut values = vec![0; len as _]; + unsafe { + error::checked(nc_get_att_ulonglong( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + + Ok(AttrValue::Ulonglongs(values)) + } + }, + NC_FLOAT => match attlen { + 1 => { + let mut value = 0.0; + unsafe { + error::checked(nc_get_att_float( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Float(value)) + } + len => { + let mut values = vec![0.0; len as _]; + unsafe { + error::checked(nc_get_att_float( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Floats(values)) + } + }, + NC_DOUBLE => match attlen { + 1 => { + let mut value = 0.0; + unsafe { + error::checked(nc_get_att_double( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + &mut value, + ))?; + } + Ok(AttrValue::Double(value)) + } + len => { + let mut values = vec![0.0; len as _]; + unsafe { + error::checked(nc_get_att_double( + self.ncid, + self.varid, + self.name.as_ptr() as *const _, + values.as_mut_ptr(), + ))?; + } + Ok(AttrValue::Doubles(values)) + } + }, NC_CHAR => { - let mut lentext = 0; - unsafe { - error::checked(nc_inq_attlen( - self.ncid, - self.varid, - self.name.as_ptr() as *const _, - &mut lentext, - ))?; - } - let mut buf: Vec = vec![0; lentext]; + let lentext = attlen; + let mut buf: Vec = vec![0; lentext as _]; unsafe { error::checked(nc_get_att_text( self.ncid, @@ -268,35 +415,11 @@ impl<'a> Iterator for AttributeIterator<'a> { return Some(Err(e)); } } - let mut atttype = 0; - unsafe { - if let Err(e) = error::checked(nc_inq_atttype( - self.ncid, - self.varid.unwrap_or(NC_GLOBAL), - name.as_ptr() as *const _, - &mut atttype, - )) { - return Some(Err(e)); - } - } - let mut attlen = 0; - unsafe { - if let Err(e) = error::checked(nc_inq_attlen( - self.ncid, - self.varid.unwrap_or(NC_GLOBAL), - name.as_ptr() as *const _, - &mut attlen, - )) { - return Some(Err(e)); - } - } let att = Attribute { name, ncid: self.ncid, varid: self.varid.unwrap_or(NC_GLOBAL), - attlen: attlen as _, - atttype, _marker: PhantomData, }; @@ -311,15 +434,25 @@ impl<'a> Iterator for AttributeIterator<'a> { #[derive(Debug, Clone, PartialEq)] pub enum AttrValue { Uchar(u8), + Uchars(Vec), Schar(i8), + Schars(Vec), Ushort(u16), + Ushorts(Vec), Short(i16), + Shorts(Vec), Uint(u32), + Uints(Vec), Int(i32), + Ints(Vec), Ulonglong(u64), + Ulonglongs(Vec), Longlong(i64), + Longlongs(Vec), Float(f32), + Floats(Vec), Double(f64), + Doubles(Vec), Str(String), } @@ -341,59 +474,125 @@ impl<'a> Attribute<'a> { }; let _l = LOCK.lock().unwrap(); - let atttype; error::checked(unsafe { match val { AttrValue::Uchar(x) => { - atttype = NC_UBYTE; - nc_put_att_uchar(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_uchar(ncid, varid, cname.as_ptr() as *const _, NC_UBYTE, 1, &x) } + AttrValue::Uchars(x) => nc_put_att_uchar( + ncid, + varid, + cname.as_ptr() as *const _, + NC_UBYTE, + x.len(), + x.as_ptr(), + ), AttrValue::Schar(x) => { - atttype = NC_BYTE; - nc_put_att_schar(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_schar(ncid, varid, cname.as_ptr() as *const _, NC_BYTE, 1, &x) } + AttrValue::Schars(x) => nc_put_att_schar( + ncid, + varid, + cname.as_ptr() as *const _, + NC_BYTE, + x.len(), + x.as_ptr(), + ), AttrValue::Ushort(x) => { - atttype = NC_USHORT; - nc_put_att_ushort(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_ushort(ncid, varid, cname.as_ptr() as *const _, NC_USHORT, 1, &x) } + AttrValue::Ushorts(x) => nc_put_att_ushort( + ncid, + varid, + cname.as_ptr() as *const _, + NC_USHORT, + x.len(), + x.as_ptr(), + ), AttrValue::Short(x) => { - atttype = NC_SHORT; - nc_put_att_short(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_short(ncid, varid, cname.as_ptr() as *const _, NC_SHORT, 1, &x) } + AttrValue::Shorts(x) => nc_put_att_short( + ncid, + varid, + cname.as_ptr() as *const _, + NC_SHORT, + x.len(), + x.as_ptr(), + ), AttrValue::Uint(x) => { - atttype = NC_UINT; - nc_put_att_uint(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_uint(ncid, varid, cname.as_ptr() as *const _, NC_UINT, 1, &x) } + AttrValue::Uints(x) => nc_put_att_uint( + ncid, + varid, + cname.as_ptr() as *const _, + NC_UINT, + x.len(), + x.as_ptr(), + ), AttrValue::Int(x) => { - atttype = NC_INT; - nc_put_att_int(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_int(ncid, varid, cname.as_ptr() as *const _, NC_INT, 1, &x) } + AttrValue::Ints(x) => nc_put_att_int( + ncid, + varid, + cname.as_ptr() as *const _, + NC_INT, + x.len(), + x.as_ptr(), + ), AttrValue::Ulonglong(x) => { - atttype = NC_UINT64; - nc_put_att_ulonglong(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_ulonglong(ncid, varid, cname.as_ptr() as *const _, NC_UINT64, 1, &x) } + AttrValue::Ulonglongs(x) => nc_put_att_ulonglong( + ncid, + varid, + cname.as_ptr() as *const _, + NC_UINT64, + x.len(), + x.as_ptr(), + ), AttrValue::Longlong(x) => { - atttype = NC_INT64; - nc_put_att_longlong(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_longlong(ncid, varid, cname.as_ptr() as *const _, NC_INT64, 1, &x) } + AttrValue::Longlongs(x) => nc_put_att_longlong( + ncid, + varid, + cname.as_ptr() as *const _, + NC_INT64, + x.len(), + x.as_ptr(), + ), AttrValue::Float(x) => { - atttype = NC_FLOAT; - nc_put_att_float(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) + nc_put_att_float(ncid, varid, cname.as_ptr() as *const _, NC_FLOAT, 1, &x) } + AttrValue::Floats(x) => nc_put_att_float( + ncid, + varid, + cname.as_ptr() as *const _, + NC_FLOAT, + x.len(), + x.as_ptr(), + ), AttrValue::Double(x) => { - atttype = NC_DOUBLE; - nc_put_att_double(ncid, varid, cname.as_ptr() as *const _, atttype, 1, &x) - } - AttrValue::Str(ref x) => { - atttype = NC_STRING; - nc_put_att_text( - ncid, - varid, - cname.as_ptr() as *const _, - x.len(), - x.as_ptr() as *const _, - ) + nc_put_att_double(ncid, varid, cname.as_ptr() as *const _, NC_DOUBLE, 1, &x) } + AttrValue::Doubles(x) => nc_put_att_double( + ncid, + varid, + cname.as_ptr() as *const _, + NC_DOUBLE, + x.len(), + x.as_ptr(), + ), + AttrValue::Str(ref x) => nc_put_att_text( + ncid, + varid, + cname.as_ptr() as *const _, + x.len(), + x.as_ptr() as *const _, + ), } })?; @@ -401,8 +600,6 @@ impl<'a> Attribute<'a> { name: cname, ncid, varid, - atttype, - attlen: 1, _marker: PhantomData, }) } @@ -421,16 +618,15 @@ impl<'a> Attribute<'a> { attname }; let _l = LOCK.lock().unwrap(); - let e; - let mut xtype = 0; - unsafe { - e = nc_inq_atttype( + let e = unsafe { + // Checking whether the variable exists by probing for its id + nc_inq_attid( ncid, varid.unwrap_or(NC_GLOBAL), attname.as_ptr() as *const _, - &mut xtype, - ); - } + std::ptr::null_mut(), + ) + }; if e == NC_ENOTATT { return Ok(None); } @@ -449,8 +645,6 @@ impl<'a> Attribute<'a> { name: attname, ncid: ncid, varid: varid.unwrap_or(NC_GLOBAL), - atttype: xtype, - attlen: xlen as _, _marker: PhantomData, })) } @@ -462,51 +656,101 @@ impl From for AttrValue { Self::Uchar(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Uchars(x) + } +} impl From for AttrValue { fn from(x: i8) -> Self { Self::Schar(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Schars(x) + } +} impl From for AttrValue { fn from(x: u16) -> Self { Self::Ushort(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Ushorts(x) + } +} impl From for AttrValue { fn from(x: i16) -> Self { Self::Short(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Shorts(x) + } +} impl From for AttrValue { fn from(x: u32) -> Self { Self::Uint(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Uints(x) + } +} impl From for AttrValue { fn from(x: i32) -> Self { Self::Int(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Ints(x) + } +} impl From for AttrValue { fn from(x: u64) -> Self { Self::Ulonglong(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Ulonglongs(x) + } +} impl From for AttrValue { fn from(x: i64) -> Self { Self::Longlong(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Longlongs(x) + } +} impl From for AttrValue { fn from(x: f32) -> Self { Self::Float(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Floats(x) + } +} impl From for AttrValue { fn from(x: f64) -> Self { Self::Double(x) } } +impl From> for AttrValue { + fn from(x: Vec) -> Self { + Self::Doubles(x) + } +} impl From<&str> for AttrValue { fn from(x: &str) -> Self { Self::Str(x.to_string()) diff --git a/tests/attributes.rs b/tests/attributes.rs index 3696d0f..eaf6a94 100644 --- a/tests/attributes.rs +++ b/tests/attributes.rs @@ -276,3 +276,64 @@ fn all_attr_types() { ); } } + +#[test] +fn multi_attributes() { + let d = tempfile::tempdir().unwrap(); + let path = d.path().join("multi_attributes"); + { + let mut file = netcdf::create(&path).unwrap(); + file.add_attribute("u8s", vec![1_u8, 2, 3, 4]).unwrap(); + file.add_attribute("i8s", vec![1_i8, 2, 3, 4]).unwrap(); + file.add_attribute("u16s", vec![1_u16, 2, 3, 4]).unwrap(); + file.add_attribute("i16s", vec![1_i16, 2, 3, 4]).unwrap(); + file.add_attribute("u32s", vec![1_u32, 2, 3, 4]).unwrap(); + file.add_attribute("i32s", vec![1_i32, 2, 3, 4]).unwrap(); + file.add_attribute("u64s", vec![1_u64, 2, 3, 4]).unwrap(); + file.add_attribute("i64s", vec![1_i64, 2, 3, 4]).unwrap(); + file.add_attribute("f32s", vec![1.0_f32, 2.0, 3.0, 4.0]) + .unwrap(); + file.add_attribute("f64s", vec![1.0_f64, 2.0, 3.0, 4.0]) + .unwrap(); + } + let file = netcdf::open(path).unwrap(); + let mut atts = 0; + for att in file.attributes().unwrap() { + let att = att.unwrap(); + match att.name().unwrap() { + "u8s" => { + assert_eq!(att.value().unwrap(), vec![1_u8, 2, 3, 4].into()); + } + "i8s" => { + assert_eq!(att.value().unwrap(), vec![1_i8, 2, 3, 4].into()); + } + "u16s" => { + assert_eq!(att.value().unwrap(), vec![1_u16, 2, 3, 4].into()); + } + "i16s" => { + assert_eq!(att.value().unwrap(), vec![1_i16, 2, 3, 4].into()); + } + "u32s" => { + assert_eq!(att.value().unwrap(), vec![1_u32, 2, 3, 4].into()); + } + "i32s" => { + assert_eq!(att.value().unwrap(), vec![1_i32, 2, 3, 4].into()); + } + "u64s" => { + assert_eq!(att.value().unwrap(), vec![1_u64, 2, 3, 4].into()); + } + "i64s" => { + assert_eq!(att.value().unwrap(), vec![1_i64, 2, 3, 4].into()); + } + "f32s" => { + assert_eq!(att.value().unwrap(), vec![1.0_f32, 2.0, 3.0, 4.0].into()); + } + "f64s" => { + assert_eq!(att.value().unwrap(), vec![1.0_f64, 2.0, 3.0, 4.0].into()); + } + name => panic!("{} not covered", name), + } + atts += 1; + } + assert_eq!(atts, 10); +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 1134692..cbba7bb 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -5,4 +5,3 @@ pub(crate) fn test_location() -> std::path::PathBuf { let mnf_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()); Path::new(&mnf_dir).join("tests").join("testdata") } -