Skip to main content

conspire/io/netcdf/
mod.rs

1#[cfg(test)]
2mod test;
3
4pub(super) mod base;
5pub(super) mod format;
6pub(super) mod from;
7pub(super) mod variable;
8
9pub use from::NetCdfError;
10
11use format::{Attribute, DimSpec, Parsed, VarSpec};
12use std::{
13    ffi::{CString, NulError},
14    fs::File,
15    sync::{Mutex, MutexGuard},
16};
17
18static NC_LOCK: Mutex<()> = Mutex::new(());
19
20pub(crate) fn nc_lock() -> MutexGuard<'static, ()> {
21    NC_LOCK.lock().unwrap_or_else(|error| error.into_inner())
22}
23
24pub(crate) fn reject_nul(name: &str) -> Result<(), NulError> {
25    CString::new(name).map(|_| ())
26}
27
28/// A netCDF file, open for either writing or reading.
29///
30/// Written files use classic CDF-5 ("64-bit data"). On read, CDF-1, CDF-2 and
31/// CDF-5 are parsed directly, and netCDF-4 (HDF5) files are read through a
32/// built-in reader covering the subset an Exodus mesh uses. Only fixed-size
33/// variables of `i32` / `f32` / `f64` are supported.
34pub struct NetCDF {
35    state: State,
36}
37
38enum State {
39    Write(Writer),
40    Read(Reader),
41}
42
43struct Writer {
44    path: String,
45    dims: Vec<DimSpec>,
46    global_attributes: Vec<Attribute>,
47    variables: Vec<VarBuild>,
48    // None = classic CDF-5; Some(n) = netCDF-4 with n chunk-compression threads.
49    netcdf4: Option<usize>,
50    output: Option<Output>,
51}
52
53struct VarBuild {
54    name: String,
55    xtype: i32,
56    dim_names: Vec<String>,
57    attributes: Vec<Attribute>,
58}
59
60struct Output {
61    file: File,
62    variables: Vec<VarSpec>,
63    data: Vec<Vec<u8>>,
64}
65
66struct Reader {
67    bytes: Vec<u8>,
68    parsed: Parsed,
69}
70
71impl Drop for NetCDF {
72    fn drop(&mut self) {
73        self.close();
74    }
75}
76
77pub trait DefineVariable {
78    fn define_variable<T: NcType>(
79        &mut self,
80        name: &str,
81        ndims: usize,
82        dim_names: &[&str],
83    ) -> Result<(), NulError>;
84}
85
86pub trait PutVariable {
87    fn put_variable<T: NcType>(&mut self, name: &str, data: &[T]) -> Result<(), NulError>;
88}
89
90pub trait GetVariable {
91    fn get_variable<T: NcType>(&self, name: &str, len: usize) -> Result<Vec<T>, NulError>;
92    fn try_get_variable<T: NcType>(
93        &self,
94        name: &str,
95        len: usize,
96    ) -> Result<Option<Vec<T>>, NulError>;
97    /// Read the hyperslab `start .. start + count` (element coordinates, one
98    /// entry per dimension) of a variable, decompressing only the chunks it
99    /// touches.
100    fn get_variable_slice<T: NcType>(
101        &self,
102        name: &str,
103        start: &[usize],
104        count: &[usize],
105    ) -> Result<Vec<T>, NulError>;
106}
107
108/// # Safety
109///
110/// `SIZE` must equal `size_of::<Self>()`, `Self` must have no padding, and every
111/// bit pattern of that width must be a valid `Self`.
112pub unsafe trait NcType: Default + Copy {
113    const XTYPE: i32;
114    const SIZE: usize;
115    fn xdr_swap(src: &[u8], dst: &mut [u8]);
116}
117
118#[inline]
119fn swap_words<const N: usize>(src: &[u8], dst: &mut [u8], swap: impl Fn([u8; N]) -> [u8; N]) {
120    let (src, _) = src.as_chunks::<N>();
121    let (dst, _) = dst.as_chunks_mut::<N>();
122    for (s, d) in src.iter().zip(dst) {
123        *d = swap(*s);
124    }
125}
126
127unsafe impl NcType for i32 {
128    const XTYPE: i32 = format::NC_INT;
129    const SIZE: usize = 4;
130    #[inline]
131    fn xdr_swap(src: &[u8], dst: &mut [u8]) {
132        swap_words::<4>(src, dst, |w| u32::from_ne_bytes(w).to_be_bytes());
133    }
134}
135
136unsafe impl NcType for f32 {
137    const XTYPE: i32 = format::NC_FLOAT;
138    const SIZE: usize = 4;
139    #[inline]
140    fn xdr_swap(src: &[u8], dst: &mut [u8]) {
141        swap_words::<4>(src, dst, |w| u32::from_ne_bytes(w).to_be_bytes());
142    }
143}
144
145unsafe impl NcType for f64 {
146    const XTYPE: i32 = format::NC_DOUBLE;
147    const SIZE: usize = 8;
148    #[inline]
149    fn xdr_swap(src: &[u8], dst: &mut [u8]) {
150        swap_words::<8>(src, dst, |w| u64::from_ne_bytes(w).to_be_bytes());
151    }
152}