diff --git a/netcdf/src/putget.rs b/netcdf/src/putget.rs index 533dc68..e08107c 100644 --- a/netcdf/src/putget.rs +++ b/netcdf/src/putget.rs @@ -184,6 +184,34 @@ pub(crate) fn get_vars( get_vars_mono(var, tp, start, count, stride, values.cast()) } +/// Non-typechecked version of get_vars +/// to support getting a bag of bytes +pub fn get_raw_values_into( + variable: &crate::Variable, + buffer: &mut [u8], + extents: crate::Extents, +) -> crate::error::Result<()> { + let dims = variable.dimensions(); + let (start, count, stride) = extents.get_start_count_stride(dims)?; + let number_of_elements = count.iter().copied().fold(1_usize, usize::saturating_mul); + let varsize = variable.vartype().size(); + if number_of_elements * varsize != buffer.len() { + return Err("Buffer is not of requisite size".into()); + } + checked_with_lock(|| unsafe { + netcdf_sys::nc_get_vars( + variable.ncid, + variable.varid, + start.as_ptr(), + count.as_ptr(), + stride.as_ptr(), + buffer.as_mut_ptr().cast(), + ) + })?; + + Ok(()) +} + #[allow(clippy::too_many_lines)] fn put_vars_mono( var: &mut crate::VariableMut, diff --git a/netcdf/src/variable.rs b/netcdf/src/variable.rs index a432e23..d20cabc 100644 --- a/netcdf/src/variable.rs +++ b/netcdf/src/variable.rs @@ -537,6 +537,42 @@ impl<'g> Variable<'g> { let extents: Extents = extents.try_into().map_err(Into::into)?; self.values_to_mono(buffer, &extents) } + + /// Fetches variable and returns the bytes. + /// It is up to the caller to decide what to do with these bytes, + /// including interpretation and freeing memory if + /// this is a vlen/string type + pub fn get_raw_values(&self, extents: E) -> error::Result> + where + E: TryInto, + E::Error: Into, + { + let extents: Extents = extents.try_into().map_err(Into::into)?; + let dims = self.dimensions(); + let (_, count, _) = extents.get_start_count_stride(dims)?; + let number_of_elements = count.iter().copied().fold(1_usize, usize::saturating_mul); + let varsize = self.vartype().size(); + let mut buffer = vec![0_u8; number_of_elements * varsize]; + + super::putget::get_raw_values_into(self, &mut buffer, extents)?; + + Ok(buffer) + } + + /// Fetches variable into provided buffer. + /// This functions returns bytes and it is up to the caller to + /// decide what to do with it, including freeing memory if + /// this is a vlen/string type + pub fn get_raw_values_into(&self, buffer: &mut [u8], extents: E) -> error::Result<()> + where + E: TryInto, + E::Error: Into, + { + let extents: Extents = extents.try_into().map_err(Into::into)?; + super::putget::get_raw_values_into(self, buffer, extents)?; + + Ok(()) + } } impl<'g> VariableMut<'g> { diff --git a/netcdf/tests/common/mod.rs b/netcdf/tests/common/mod.rs index cbba7bb..3f661d5 100644 --- a/netcdf/tests/common/mod.rs +++ b/netcdf/tests/common/mod.rs @@ -1,4 +1,5 @@ /// Get location of the test files +#[allow(dead_code)] pub(crate) fn test_location() -> std::path::PathBuf { use std::path::Path; diff --git a/netcdf/tests/lib.rs b/netcdf/tests/lib.rs index fe62a93..6a71ccc 100644 --- a/netcdf/tests/lib.rs +++ b/netcdf/tests/lib.rs @@ -1766,3 +1766,32 @@ fn close_file() { let f = netcdf::open(path).unwrap(); f.close().unwrap(); } + +#[test] +fn read_raw_values() { + let path = test_location().join("sfc_pres_temp.nc"); + let file = netcdf::open(path).unwrap(); + + let var = file.variable("pressure").unwrap(); + let d_lat = 6; + let d_lon = 12; + + let buffer = var.get_raw_values(..).unwrap(); + assert_eq!(buffer.len(), d_lat * d_lon * std::mem::size_of::()); + let buffer = var.get_raw_values((0, ..)).unwrap(); + assert_eq!(buffer.len(), 1 * d_lon * std::mem::size_of::()); + let buffer = var.get_raw_values((.., 0)).unwrap(); + assert_eq!(buffer.len(), d_lat * 1 * std::mem::size_of::()); + + let mut buffer = vec![0; d_lat * d_lon * std::mem::size_of::()]; + var.get_raw_values_into(&mut buffer, ..).unwrap(); + + var.get_raw_values_into(&mut buffer[..d_lon * 1 * 4], (0, ..)) + .unwrap(); + + // Mismatched buffers + var.get_raw_values_into(&mut buffer[..d_lon * 1 * 4 - 1], (0, ..)) + .unwrap_err(); + var.get_raw_values_into(&mut buffer[..d_lon * 1 * 4 + 1], (0, ..)) + .unwrap_err(); +}