support multi-valued attributes

This commit is contained in:
Magnus Ulimoen 2020-01-02 00:07:06 +01:00
parent 6d8e9a0a04
commit f022644854
3 changed files with 503 additions and 199 deletions

View File

@ -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, "<<not utf8 name>>")?;
}
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<AttrValue> {
let mut typ = 0;
fn num_elems(&self) -> error::Result<usize> {
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<nc_type> {
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<AttrValue> {
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<u8> = vec![0; lentext];
let lentext = attlen;
let mut buf: Vec<u8> = 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<u8>),
Schar(i8),
Schars(Vec<i8>),
Ushort(u16),
Ushorts(Vec<u16>),
Short(i16),
Shorts(Vec<i16>),
Uint(u32),
Uints(Vec<u32>),
Int(i32),
Ints(Vec<i32>),
Ulonglong(u64),
Ulonglongs(Vec<u64>),
Longlong(i64),
Longlongs(Vec<i64>),
Float(f32),
Floats(Vec<f32>),
Double(f64),
Doubles(Vec<f64>),
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<u8> for AttrValue {
Self::Uchar(x)
}
}
impl From<Vec<u8>> for AttrValue {
fn from(x: Vec<u8>) -> Self {
Self::Uchars(x)
}
}
impl From<i8> for AttrValue {
fn from(x: i8) -> Self {
Self::Schar(x)
}
}
impl From<Vec<i8>> for AttrValue {
fn from(x: Vec<i8>) -> Self {
Self::Schars(x)
}
}
impl From<u16> for AttrValue {
fn from(x: u16) -> Self {
Self::Ushort(x)
}
}
impl From<Vec<u16>> for AttrValue {
fn from(x: Vec<u16>) -> Self {
Self::Ushorts(x)
}
}
impl From<i16> for AttrValue {
fn from(x: i16) -> Self {
Self::Short(x)
}
}
impl From<Vec<i16>> for AttrValue {
fn from(x: Vec<i16>) -> Self {
Self::Shorts(x)
}
}
impl From<u32> for AttrValue {
fn from(x: u32) -> Self {
Self::Uint(x)
}
}
impl From<Vec<u32>> for AttrValue {
fn from(x: Vec<u32>) -> Self {
Self::Uints(x)
}
}
impl From<i32> for AttrValue {
fn from(x: i32) -> Self {
Self::Int(x)
}
}
impl From<Vec<i32>> for AttrValue {
fn from(x: Vec<i32>) -> Self {
Self::Ints(x)
}
}
impl From<u64> for AttrValue {
fn from(x: u64) -> Self {
Self::Ulonglong(x)
}
}
impl From<Vec<u64>> for AttrValue {
fn from(x: Vec<u64>) -> Self {
Self::Ulonglongs(x)
}
}
impl From<i64> for AttrValue {
fn from(x: i64) -> Self {
Self::Longlong(x)
}
}
impl From<Vec<i64>> for AttrValue {
fn from(x: Vec<i64>) -> Self {
Self::Longlongs(x)
}
}
impl From<f32> for AttrValue {
fn from(x: f32) -> Self {
Self::Float(x)
}
}
impl From<Vec<f32>> for AttrValue {
fn from(x: Vec<f32>) -> Self {
Self::Floats(x)
}
}
impl From<f64> for AttrValue {
fn from(x: f64) -> Self {
Self::Double(x)
}
}
impl From<Vec<f64>> for AttrValue {
fn from(x: Vec<f64>) -> Self {
Self::Doubles(x)
}
}
impl From<&str> for AttrValue {
fn from(x: &str) -> Self {
Self::Str(x.to_string())

View File

@ -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);
}

View File

@ -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")
}