#![allow(clippy::similar_names)]
use super::error;
use netcdf_sys::*;
use std::convert::TryInto;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_char;
#[derive(Clone)]
pub struct Attribute<'a> {
pub(crate) name: [u8; NC_MAX_NAME as usize + 1],
pub(crate) ncid: nc_type,
pub(crate) varid: nc_type,
pub(crate) _marker: PhantomData<&'a nc_type>,
}
impl<'a> std::fmt::Debug for Attribute<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "name: {}", self.name())?;
write!(f, "ncid: {}", self.ncid)?;
write!(f, "varid: {}", self.varid)
}
}
impl<'a> Attribute<'a> {
pub fn name(&self) -> &str {
let zeropos = self
.name
.iter()
.position(|&x| x == 0)
.unwrap_or(self.name.len());
std::str::from_utf8(&self.name[..zeropos])
.expect("Attribute name contains invalid sequence")
}
fn num_elems(&self) -> error::Result<usize> {
let mut nelems = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_inq_attlen(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut nelems,
)
}))?;
}
Ok(nelems as _)
}
fn typ(&self) -> error::Result<nc_type> {
let mut atttype = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_inq_atttype(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut atttype,
)
}))?;
}
Ok(atttype)
}
#[allow(clippy::too_many_lines)]
pub fn value(&self) -> error::Result<AttrValue> {
let attlen = self.num_elems()?;
let typ = self.typ()?;
match typ {
NC_UBYTE => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_uchar(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Uchar(value))
}
len => {
let mut values = vec![0_u8; len as usize];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_uchar(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Uchars(values))
}
},
NC_BYTE => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_schar(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Schar(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_schar(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Schars(values))
}
},
NC_SHORT => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_short(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Short(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_short(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Shorts(values))
}
},
NC_USHORT => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_ushort(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Ushort(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_ushort(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Ushorts(values))
}
},
NC_INT => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_int(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Int(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_int(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Ints(values))
}
},
NC_UINT => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_uint(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Uint(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_uint(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Uints(values))
}
},
NC_INT64 => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_longlong(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Longlong(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_longlong(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Longlongs(values))
}
},
NC_UINT64 => match attlen {
1 => {
let mut value = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_ulonglong(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Ulonglong(value))
}
len => {
let mut values = vec![0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_ulonglong(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Ulonglongs(values))
}
},
NC_FLOAT => match attlen {
1 => {
let mut value = 0.0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_float(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Float(value))
}
len => {
let mut values = vec![0.0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_float(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Floats(values))
}
},
NC_DOUBLE => match attlen {
1 => {
let mut value = 0.0;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_double(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
&mut value,
)
}))?;
}
Ok(AttrValue::Double(value))
}
len => {
let mut values = vec![0.0; len as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_double(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
values.as_mut_ptr(),
)
}))?;
}
Ok(AttrValue::Doubles(values))
}
},
NC_CHAR => {
let lentext = attlen;
let mut buf: Vec<u8> = vec![0; lentext as _];
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_text(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
buf.as_mut_ptr().cast(),
)
}))?;
}
let pos = buf.iter().position(|&x| x == 0).unwrap_or(buf.len());
Ok(AttrValue::Str(String::from(String::from_utf8_lossy(
&buf[..pos],
))))
}
NC_STRING => {
let mut buf: Vec<*mut c_char> = vec![std::ptr::null_mut(); attlen];
let result;
unsafe {
error::checked(super::with_lock(|| {
nc_get_att_string(
self.ncid,
self.varid,
self.name.as_ptr().cast(),
buf.as_mut_ptr().cast(),
)
}))?;
result = buf
.iter()
.map(|cstr_pointer| {
if cstr_pointer.is_null() {
String::new()
} else {
CStr::from_ptr(*cstr_pointer).to_string_lossy().to_string()
}
})
.collect();
super::with_lock(|| nc_free_string(attlen, buf.as_mut_ptr()));
}
Ok(AttrValue::Strs(result))
}
x => Err(error::Error::TypeUnknown(x)),
}
}
}
pub(crate) struct AttributeIterator<'a> {
ncid: nc_type,
varid: Option<nc_type>,
natts: usize,
current_natt: usize,
_marker: PhantomData<&'a nc_type>,
}
impl<'a> AttributeIterator<'a> {
pub(crate) fn new(ncid: nc_type, varid: Option<nc_type>) -> error::Result<Self> {
let mut natts = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_inq_varnatts(ncid, varid.unwrap_or(NC_GLOBAL), &mut natts)
}))?;
}
Ok(Self {
ncid,
varid,
natts: natts.try_into()?,
current_natt: 0,
_marker: PhantomData,
})
}
}
impl<'a> Iterator for AttributeIterator<'a> {
type Item = error::Result<Attribute<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_natt >= self.natts {
return None;
}
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
unsafe {
if let Err(e) = error::checked(super::with_lock(|| {
nc_inq_attname(
self.ncid,
self.varid.unwrap_or(NC_GLOBAL),
self.current_natt.try_into().unwrap(),
name.as_mut_ptr().cast(),
)
})) {
return Some(Err(e));
}
}
let att = Attribute {
name,
ncid: self.ncid,
varid: self.varid.unwrap_or(NC_GLOBAL),
_marker: PhantomData,
};
self.current_natt += 1;
Some(Ok(att))
}
}
#[allow(missing_docs)]
#[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),
Strs(Vec<String>),
}
impl<'a> Attribute<'a> {
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::too_many_lines)]
pub(crate) fn put(
ncid: nc_type,
varid: nc_type,
name: &str,
val: AttrValue,
) -> error::Result<Self> {
let cname = super::utils::short_name_to_bytes(name)?;
error::checked(unsafe {
match val {
AttrValue::Uchar(x) => super::with_lock(|| {
nc_put_att_uchar(ncid, varid, cname.as_ptr().cast(), NC_UBYTE, 1, &x)
}),
AttrValue::Uchars(x) => super::with_lock(|| {
nc_put_att_uchar(
ncid,
varid,
cname.as_ptr().cast(),
NC_UBYTE,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Schar(x) => super::with_lock(|| {
nc_put_att_schar(ncid, varid, cname.as_ptr().cast(), NC_BYTE, 1, &x)
}),
AttrValue::Schars(x) => super::with_lock(|| {
nc_put_att_schar(
ncid,
varid,
cname.as_ptr().cast(),
NC_BYTE,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Ushort(x) => super::with_lock(|| {
nc_put_att_ushort(ncid, varid, cname.as_ptr().cast(), NC_USHORT, 1, &x)
}),
AttrValue::Ushorts(x) => super::with_lock(|| {
nc_put_att_ushort(
ncid,
varid,
cname.as_ptr().cast(),
NC_USHORT,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Short(x) => super::with_lock(|| {
nc_put_att_short(ncid, varid, cname.as_ptr().cast(), NC_SHORT, 1, &x)
}),
AttrValue::Shorts(x) => super::with_lock(|| {
nc_put_att_short(
ncid,
varid,
cname.as_ptr().cast(),
NC_SHORT,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Uint(x) => super::with_lock(|| {
nc_put_att_uint(ncid, varid, cname.as_ptr().cast(), NC_UINT, 1, &x)
}),
AttrValue::Uints(x) => super::with_lock(|| {
nc_put_att_uint(
ncid,
varid,
cname.as_ptr().cast(),
NC_UINT,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Int(x) => super::with_lock(|| {
nc_put_att_int(ncid, varid, cname.as_ptr().cast(), NC_INT, 1, &x)
}),
AttrValue::Ints(x) => super::with_lock(|| {
nc_put_att_int(
ncid,
varid,
cname.as_ptr().cast(),
NC_INT,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Ulonglong(x) => super::with_lock(|| {
nc_put_att_ulonglong(ncid, varid, cname.as_ptr().cast(), NC_UINT64, 1, &x)
}),
AttrValue::Ulonglongs(x) => super::with_lock(|| {
nc_put_att_ulonglong(
ncid,
varid,
cname.as_ptr().cast(),
NC_UINT64,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Longlong(x) => super::with_lock(|| {
nc_put_att_longlong(ncid, varid, cname.as_ptr().cast(), NC_INT64, 1, &x)
}),
AttrValue::Longlongs(x) => super::with_lock(|| {
nc_put_att_longlong(
ncid,
varid,
cname.as_ptr().cast(),
NC_INT64,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Float(x) => super::with_lock(|| {
nc_put_att_float(ncid, varid, cname.as_ptr().cast(), NC_FLOAT, 1, &x)
}),
AttrValue::Floats(x) => super::with_lock(|| {
nc_put_att_float(
ncid,
varid,
cname.as_ptr().cast(),
NC_FLOAT,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Double(x) => super::with_lock(|| {
nc_put_att_double(ncid, varid, cname.as_ptr().cast(), NC_DOUBLE, 1, &x)
}),
AttrValue::Doubles(x) => super::with_lock(|| {
nc_put_att_double(
ncid,
varid,
cname.as_ptr().cast(),
NC_DOUBLE,
x.len(),
x.as_ptr(),
)
}),
AttrValue::Str(ref x) => super::with_lock(|| {
nc_put_att_text(
ncid,
varid,
cname.as_ptr().cast(),
x.len(),
x.as_ptr().cast(),
)
}),
AttrValue::Strs(ref x) => {
let cstrings: Vec<CString> = x
.iter()
.map(String::as_str)
.map(CString::new)
.collect::<Result<Vec<CString>, _>>()?;
let cstring_pointers: Vec<*const c_char> =
cstrings.iter().map(|cs| cs.as_ptr()).collect();
super::with_lock(|| {
nc_put_att_string(
ncid,
varid,
cname.as_ptr().cast(),
cstring_pointers.len(),
cstring_pointers.as_ptr() as *mut *const _,
)
})
}
}
})?;
Ok(Self {
name: cname,
ncid,
varid,
_marker: PhantomData,
})
}
pub(crate) fn find_from_name(
ncid: nc_type,
varid: Option<nc_type>,
name: &str,
) -> error::Result<Option<Self>> {
let attname = {
if name.len() > NC_MAX_NAME as usize {
return Err(error::Error::Netcdf(NC_EMAXNAME));
}
let mut attname = [0_u8; NC_MAX_NAME as usize + 1];
attname[..name.len()].copy_from_slice(name.as_bytes());
attname
};
let e = unsafe {
super::with_lock(|| {
nc_inq_attid(
ncid,
varid.unwrap_or(NC_GLOBAL),
attname.as_ptr().cast(),
std::ptr::null_mut(),
)
})
};
if e == NC_ENOTATT {
return Ok(None);
}
error::checked(e)?;
Ok(Some(Attribute {
name: attname,
ncid,
varid: varid.unwrap_or(NC_GLOBAL),
_marker: PhantomData,
}))
}
}
impl From<u8> for AttrValue {
fn from(x: u8) -> Self {
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())
}
}
impl From<String> for AttrValue {
fn from(x: String) -> Self {
Self::Str(x)
}
}
impl From<Vec<String>> for AttrValue {
fn from(x: Vec<String>) -> Self {
Self::Strs(x)
}
}
impl From<&[String]> for AttrValue {
fn from(x: &[String]) -> Self {
Self::Strs(x.to_vec())
}
}
impl From<&[&str]> for AttrValue {
fn from(x: &[&str]) -> Self {
Self::Strs(x.iter().map(|&s| String::from(s)).collect())
}
}
impl From<Vec<&str>> for AttrValue {
fn from(x: Vec<&str>) -> Self {
Self::from(x.as_slice())
}
}
#[test]
fn conversion() {
let x = 1.0f32;
let _b: AttrValue = x.into();
}