use super::error;
use crate::with_lock;
use netcdf_sys::*;
use std::convert::TryInto;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BasicType {
Byte,
Ubyte,
Short,
Ushort,
Int,
Uint,
Int64,
Uint64,
Float,
Double,
}
impl BasicType {
fn size(self) -> usize {
match self {
Self::Byte | Self::Ubyte => 1,
Self::Short | Self::Ushort => 2,
Self::Int | Self::Uint | Self::Float => 4,
Self::Int64 | Self::Uint64 | Self::Double => 8,
}
}
pub(crate) fn id(self) -> nc_type {
use super::Numeric;
match self {
Self::Byte => i8::NCTYPE,
Self::Ubyte => u8::NCTYPE,
Self::Short => i16::NCTYPE,
Self::Ushort => u16::NCTYPE,
Self::Int => i32::NCTYPE,
Self::Uint => u32::NCTYPE,
Self::Int64 => i64::NCTYPE,
Self::Uint64 => u64::NCTYPE,
Self::Float => f32::NCTYPE,
Self::Double => f64::NCTYPE,
}
}
pub fn name(self) -> &'static str {
match self {
BasicType::Byte => "i8",
BasicType::Ubyte => "u8",
BasicType::Short => "i16",
BasicType::Ushort => "u16",
BasicType::Int => "i32",
BasicType::Uint => "u32",
BasicType::Int64 => "i64",
BasicType::Uint64 => "u64",
BasicType::Float => "f32",
BasicType::Double => "f64",
}
}
}
#[allow(missing_docs)]
impl BasicType {
pub fn is_i8(self) -> bool {
self == Self::Byte
}
pub fn is_u8(self) -> bool {
self == Self::Ubyte
}
pub fn is_i16(self) -> bool {
self == Self::Short
}
pub fn is_u16(self) -> bool {
self == Self::Ushort
}
pub fn is_i32(self) -> bool {
self == Self::Int
}
pub fn is_u32(self) -> bool {
self == Self::Uint
}
pub fn is_i64(self) -> bool {
self == Self::Int64
}
pub fn is_u64(self) -> bool {
self == Self::Uint64
}
pub fn is_f32(self) -> bool {
self == Self::Float
}
pub fn is_f64(self) -> bool {
self == Self::Double
}
}
#[derive(Clone, Debug)]
pub struct OpaqueType {
ncid: nc_type,
id: nc_type,
}
impl OpaqueType {
pub fn name(&self) -> String {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
error::checked(super::with_lock(|| unsafe {
nc_inq_opaque(
self.ncid,
self.id,
name.as_mut_ptr() as *mut _,
std::ptr::null_mut(),
)
}))
.unwrap();
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
String::from_utf8(name[..pos].to_vec()).unwrap()
}
pub fn size(&self) -> usize {
let mut numbytes = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_opaque(self.ncid, self.id, std::ptr::null_mut(), &mut numbytes)
}))
.unwrap();
numbytes
}
pub(crate) fn add(location: nc_type, name: &str, size: usize) -> error::Result<Self> {
let name = super::utils::short_name_to_bytes(name)?;
let mut id = 0;
error::checked(super::with_lock(|| unsafe {
nc_def_opaque(location, size, name.as_ptr() as *const _, &mut id)
}))?;
Ok(Self { ncid: location, id })
}
}
#[derive(Debug, Clone)]
pub struct VlenType {
ncid: nc_type,
id: nc_type,
}
impl VlenType {
pub fn name(&self) -> String {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
error::checked(super::with_lock(|| unsafe {
nc_inq_vlen(
self.ncid,
self.id,
name.as_mut_ptr() as *mut _,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
}))
.unwrap();
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
String::from_utf8(name[..pos].to_vec()).unwrap()
}
pub(crate) fn add<T>(location: nc_type, name: &str) -> error::Result<Self>
where
T: super::Numeric,
{
let name = super::utils::short_name_to_bytes(name)?;
let mut id = 0;
error::checked(super::with_lock(|| unsafe {
nc_def_vlen(location, name.as_ptr() as *const _, T::NCTYPE, &mut id)
}))?;
Ok(Self { ncid: location, id })
}
pub fn typ(&self) -> BasicType {
let mut bastyp = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_vlen(
self.ncid,
self.id,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut bastyp,
)
}))
.unwrap();
match bastyp {
NC_BYTE => BasicType::Byte,
NC_UBYTE => BasicType::Ubyte,
NC_SHORT => BasicType::Short,
NC_USHORT => BasicType::Ushort,
NC_INT => BasicType::Int,
NC_UINT => BasicType::Uint,
NC_INT64 => BasicType::Int64,
NC_UINT64 => BasicType::Uint64,
NC_FLOAT => BasicType::Float,
NC_DOUBLE => BasicType::Double,
_ => panic!("Did not expect typeid {} in this context", bastyp),
}
}
}
#[derive(Debug, Clone)]
pub struct EnumType {
ncid: nc_type,
id: nc_type,
}
impl EnumType {
pub(crate) fn add<T: super::Numeric>(
ncid: nc_type,
name: &str,
mappings: &[(&str, T)],
) -> error::Result<EnumType> {
let name = super::utils::short_name_to_bytes(name)?;
let mut id = 0;
error::checked(super::with_lock(|| unsafe {
nc_def_enum(ncid, T::NCTYPE, name.as_ptr() as *const _, &mut id)
}))?;
for (name, val) in mappings {
let name = super::utils::short_name_to_bytes(name)?;
error::checked(super::with_lock(|| unsafe {
nc_insert_enum(
ncid,
id,
name.as_ptr() as *const _,
val as *const T as *const _,
)
}))?;
}
Ok(Self { ncid, id })
}
pub fn typ(&self) -> BasicType {
let mut typ = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_enum(
self.ncid,
self.id,
std::ptr::null_mut(),
&mut typ,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
}))
.unwrap();
match typ {
NC_BYTE => BasicType::Byte,
NC_UBYTE => BasicType::Ubyte,
NC_SHORT => BasicType::Short,
NC_USHORT => BasicType::Ushort,
NC_INT => BasicType::Int,
NC_UINT => BasicType::Uint,
NC_INT64 => BasicType::Int64,
NC_UINT64 => BasicType::Uint64,
NC_FLOAT => BasicType::Float,
NC_DOUBLE => BasicType::Double,
_ => panic!("Did not expect typeid {} in this context", typ),
}
}
unsafe fn member_at<T: super::Numeric>(&self, idx: usize) -> error::Result<(String, T)> {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
let mut t = std::mem::MaybeUninit::<T>::uninit();
let idx = idx.try_into()?;
super::with_lock(|| {
nc_inq_enum_member(
self.ncid,
self.id,
idx,
name.as_mut_ptr() as *mut _,
t.as_mut_ptr() as *mut _,
)
});
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
let name = String::from_utf8(name[..pos].to_vec()).unwrap();
Ok((name, t.assume_init()))
}
pub fn members<'f, T: super::Numeric>(
&'f self,
) -> error::Result<impl Iterator<Item = (String, T)> + 'f> {
let mut typ = 0;
let mut nummembers = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_enum(
self.ncid,
self.id,
std::ptr::null_mut(),
&mut typ,
std::ptr::null_mut(),
&mut nummembers,
)
}))
.unwrap();
if typ != T::NCTYPE {
return Err(error::Error::TypeMismatch);
}
Ok((0..nummembers).map(move |idx| unsafe { self.member_at::<T>(idx) }.unwrap()))
}
pub fn name(&self) -> String {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
error::checked(super::with_lock(|| unsafe {
nc_inq_enum(
self.ncid,
self.id,
name.as_mut_ptr() as *mut _,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
}))
.unwrap();
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
String::from_utf8(name[..pos].to_vec()).unwrap()
}
pub fn name_from_value(&self, value: i64) -> Option<String> {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
let e = super::with_lock(|| unsafe {
nc_inq_enum_ident(self.ncid, self.id, value, name.as_mut_ptr() as *mut _)
});
if e == NC_EINVAL {
return None;
}
error::checked(e).unwrap();
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
Some(String::from_utf8(name[..pos].to_vec()).unwrap())
}
fn size(&self) -> usize {
self.typ().size()
}
}
#[derive(Debug, Clone)]
pub struct CompoundType {
ncid: nc_type,
id: nc_type,
}
impl CompoundType {
pub(crate) fn add(ncid: nc_type, name: &str) -> error::Result<CompoundBuilder> {
let name = super::utils::short_name_to_bytes(name)?;
Ok(CompoundBuilder {
ncid,
name,
size: 0,
comp: Vec::new(),
})
}
fn size(&self) -> usize {
let mut size = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_compound(
self.ncid,
self.id,
std::ptr::null_mut(),
&mut size,
std::ptr::null_mut(),
)
}))
.unwrap();
size
}
pub fn name(&self) -> String {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
error::checked(super::with_lock(|| unsafe {
nc_inq_compound(
self.ncid,
self.id,
name.as_mut_ptr() as *mut _,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
}))
.unwrap();
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
String::from_utf8(name[..pos].to_vec()).unwrap()
}
pub fn fields(&self) -> impl Iterator<Item = CompoundField> {
let ncid = self.ncid;
let parent_id = self.id;
let mut nfields = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_compound_nfields(ncid, parent_id, &mut nfields)
}))
.unwrap();
(0..nfields).map(move |x| CompoundField {
ncid,
parent: parent_id,
id: x,
})
}
}
pub struct CompoundField {
ncid: nc_type,
parent: nc_type,
id: usize,
}
impl CompoundField {
pub fn name(&self) -> String {
let mut name = [0_u8; NC_MAX_NAME as usize + 1];
let idx = self.id.try_into().unwrap();
error::checked(super::with_lock(|| unsafe {
nc_inq_compound_fieldname(self.ncid, self.parent, idx, name.as_mut_ptr() as *mut _)
}))
.unwrap();
let pos = name
.iter()
.position(|&x| x == 0)
.unwrap_or_else(|| name.len());
String::from_utf8(name[..pos].to_vec()).unwrap()
}
pub fn typ(&self) -> VariableType {
let mut typ = 0;
let id = self.id.try_into().unwrap();
error::checked(super::with_lock(|| unsafe {
nc_inq_compound_fieldtype(self.ncid, self.parent, id, &mut typ)
}))
.unwrap();
VariableType::from_id(self.ncid, typ).unwrap()
}
pub fn offset(&self) -> usize {
let mut offset = 0;
let id = self.id.try_into().unwrap();
error::checked(super::with_lock(|| unsafe {
nc_inq_compound_field(
self.ncid,
self.parent,
id,
std::ptr::null_mut(),
&mut offset,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
}))
.unwrap();
offset
}
pub fn dimensions(&self) -> Option<Vec<usize>> {
let mut num_dims = 0;
let id = self.id.try_into().unwrap();
error::checked(super::with_lock(|| unsafe {
nc_inq_compound_fieldndims(self.ncid, self.parent, id, &mut num_dims)
}))
.unwrap();
if num_dims == 0 {
return None;
}
let mut dims = vec![0; num_dims.try_into().unwrap()];
error::checked(super::with_lock(|| unsafe {
nc_inq_compound_fielddim_sizes(self.ncid, self.parent, id, dims.as_mut_ptr())
}))
.unwrap();
Some(dims.iter().map(|&x| x.try_into().unwrap()).collect())
}
}
#[must_use]
pub struct CompoundBuilder {
ncid: nc_type,
name: [u8; NC_MAX_NAME as usize + 1],
size: usize,
comp: Vec<(
VariableType,
[u8; NC_MAX_NAME as usize + 1],
Option<Vec<i32>>,
)>,
}
impl CompoundBuilder {
pub fn add_type(&mut self, name: &str, var: &VariableType) -> error::Result<&mut Self> {
self.comp
.push((var.clone(), super::utils::short_name_to_bytes(name)?, None));
self.size += var.size();
Ok(self)
}
pub fn add<T: super::Numeric>(&mut self, name: &str) -> error::Result<&mut Self> {
let var = VariableType::from_id(self.ncid, T::NCTYPE)?;
self.add_type(name, &var)
}
pub fn add_array<T: super::Numeric>(
&mut self,
name: &str,
dims: &[usize],
) -> error::Result<&mut Self> {
let var = VariableType::from_id(self.ncid, T::NCTYPE)?;
self.add_array_type(name, &var, dims)
}
pub fn add_array_type(
&mut self,
name: &str,
var: &VariableType,
dims: &[usize],
) -> error::Result<&mut Self> {
self.comp.push((
var.clone(),
super::utils::short_name_to_bytes(name)?,
Some(dims.iter().map(|&x| x.try_into().unwrap()).collect()),
));
self.size += var.size() * dims.iter().product::<usize>();
Ok(self)
}
pub fn build(self) -> error::Result<CompoundType> {
let mut id = 0;
error::checked(super::with_lock(|| unsafe {
nc_def_compound(
self.ncid,
self.size,
self.name.as_ptr() as *const _,
&mut id,
)
}))?;
let mut offset = 0;
for (typ, name, dims) in &self.comp {
match dims {
None => {
error::checked(super::with_lock(|| unsafe {
nc_insert_compound(
self.ncid,
id,
name.as_ptr() as *const _,
offset,
typ.id(),
)
}))?;
offset += typ.size();
}
Some(dims) => {
let dimlen = dims.len().try_into().unwrap();
error::checked(super::with_lock(|| unsafe {
nc_insert_array_compound(
self.ncid,
id,
name.as_ptr() as *const _,
offset,
typ.id(),
dimlen,
dims.as_ptr(),
)
}))?;
offset += typ.size()
* dims
.iter()
.map(|x: &i32| -> usize { (*x).try_into().unwrap() })
.product::<usize>();
}
}
}
Ok(CompoundType {
ncid: self.ncid,
id,
})
}
}
#[derive(Debug, Clone)]
pub enum VariableType {
Basic(BasicType),
String,
Opaque(OpaqueType),
Vlen(VlenType),
Enum(EnumType),
Compound(CompoundType),
}
impl VariableType {
pub fn as_basic(&self) -> Option<BasicType> {
match self {
Self::Basic(x) => Some(*x),
_ => None,
}
}
pub(crate) fn size(&self) -> usize {
match self {
Self::Basic(b) => b.size(),
Self::String => panic!("A string does not have a defined size"),
Self::Enum(e) => e.size(),
Self::Opaque(o) => o.size(),
Self::Vlen(_) => panic!("A variable length array does not have a defined size"),
Self::Compound(c) => c.size(),
}
}
pub(crate) fn id(&self) -> nc_type {
match self {
Self::Basic(b) => b.id(),
Self::String => NC_STRING,
Self::Enum(e) => e.id,
Self::Opaque(o) => o.id,
Self::Vlen(v) => v.id,
Self::Compound(c) => c.id,
}
}
pub fn name(&self) -> String {
match self {
Self::Basic(b) => b.name().into(),
Self::String => "string".into(),
Self::Enum(e) => e.name(),
Self::Opaque(o) => o.name(),
Self::Vlen(v) => v.name(),
Self::Compound(c) => c.name(),
}
}
}
#[allow(missing_docs)]
impl VariableType {
pub fn is_string(&self) -> bool {
matches!(self, Self::String)
}
pub fn is_i8(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_i8)
}
pub fn is_u8(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_u8)
}
pub fn is_i16(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_i16)
}
pub fn is_u16(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_u16)
}
pub fn is_i32(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_i32)
}
pub fn is_u32(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_u32)
}
pub fn is_i64(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_i64)
}
pub fn is_u64(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_u64)
}
pub fn is_f32(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_f32)
}
pub fn is_f64(&self) -> bool {
self.as_basic().map_or(false, BasicType::is_f64)
}
}
impl VariableType {
pub(crate) fn from_id(ncid: nc_type, xtype: nc_type) -> error::Result<Self> {
match xtype {
NC_BYTE => Ok(Self::Basic(BasicType::Byte)),
NC_UBYTE => Ok(Self::Basic(BasicType::Ubyte)),
NC_SHORT => Ok(Self::Basic(BasicType::Short)),
NC_USHORT => Ok(Self::Basic(BasicType::Ushort)),
NC_INT => Ok(Self::Basic(BasicType::Int)),
NC_UINT => Ok(Self::Basic(BasicType::Uint)),
NC_INT64 => Ok(Self::Basic(BasicType::Int64)),
NC_UINT64 => Ok(Self::Basic(BasicType::Uint64)),
NC_FLOAT => Ok(Self::Basic(BasicType::Float)),
NC_DOUBLE => Ok(Self::Basic(BasicType::Double)),
NC_STRING => Ok(Self::String),
xtype => {
let mut base_xtype = 0;
error::checked(super::with_lock(|| unsafe {
nc_inq_user_type(
ncid,
xtype,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut base_xtype,
)
}))?;
match base_xtype {
NC_VLEN => Ok(VlenType { ncid, id: xtype }.into()),
NC_OPAQUE => Ok(OpaqueType { ncid, id: xtype }.into()),
NC_ENUM => Ok(EnumType { ncid, id: xtype }.into()),
NC_COMPOUND => Ok(CompoundType { ncid, id: xtype }.into()),
_ => panic!("Unexpected base type: {}", base_xtype),
}
}
}
}
}
pub(crate) fn all_at_location(
ncid: nc_type,
) -> error::Result<impl Iterator<Item = error::Result<VariableType>>> {
let typeids = {
let mut num_typeids = 0;
error::checked(with_lock(|| unsafe {
nc_inq_typeids(ncid, &mut num_typeids, std::ptr::null_mut())
}))?;
let mut typeids = vec![0; num_typeids.try_into()?];
error::checked(with_lock(|| unsafe {
nc_inq_typeids(ncid, std::ptr::null_mut(), typeids.as_mut_ptr())
}))?;
typeids
};
Ok(typeids
.into_iter()
.map(move |x| VariableType::from_id(ncid, x)))
}
impl Into<VariableType> for CompoundType {
fn into(self) -> VariableType {
VariableType::Compound(self)
}
}
impl Into<VariableType> for BasicType {
fn into(self) -> VariableType {
VariableType::Basic(self)
}
}
impl Into<VariableType> for EnumType {
fn into(self) -> VariableType {
VariableType::Enum(self)
}
}
impl Into<VariableType> for VlenType {
fn into(self) -> VariableType {
VariableType::Vlen(self)
}
}
impl Into<VariableType> for OpaqueType {
fn into(self) -> VariableType {
VariableType::Opaque(self)
}
}