Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added resources/example_xy_little_endian_record.npy
Binary file not shown.
Binary file added resources/example_xy_little_endian_record_2d.npy
Binary file not shown.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ pub use crate::npy::{
};
#[cfg(feature = "npz")]
pub use crate::npz::{NpzReader, NpzWriter, ReadNpzError, WriteNpzError};
pub use py_literal;
2 changes: 2 additions & 0 deletions src/npy/elements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,5 @@ mod bool;
#[cfg(feature = "num-complex-0_4")]
mod complex;
mod num;
mod record;
pub use record::RecordFromSlice;
30 changes: 30 additions & 0 deletions src/npy/elements/record.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Currently only little-endian is supported

use crate::{ReadDataError, ReadableElement};
// pub use ndarray_derive::RecordFromSlice;
use py_literal;

pub trait RecordFromSlice: Sized {
fn compatible_schema(type_descr: &py_literal::Value) -> bool;

fn from_raw_slice<R: std::io::Read>(reader: &mut R) -> Result<Self, ReadDataError>;
}

impl<T: RecordFromSlice> ReadableElement for T {
fn read_to_end_exact_vec<R: std::io::Read>(
mut reader: R,
type_desc: &py_literal::Value,
len: usize,
) -> Result<Vec<Self>, ReadDataError> {
if !T::compatible_schema(type_desc) {
return Err(ReadDataError::WrongDescriptor(type_desc.clone()));
}

let mut out = Vec::with_capacity(len);
for _ in 0..len {
out.push(RecordFromSlice::from_raw_slice(&mut reader)?);
}

Ok(out)
}
}
1 change: 1 addition & 0 deletions src/npy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

mod elements;
pub mod header;
pub use elements::RecordFromSlice;

use self::header::{
FormatHeaderError, Header, Layout, ParseHeaderError, ReadHeaderError, WriteHeaderError,
Expand Down
61 changes: 61 additions & 0 deletions tests/integration/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,64 @@ fn zeroed() {
assert_eq!(arr, Array3::<i32>::zeros(SHAPE));
assert!(arr.is_standard_layout());
}

#[cfg(target_endian = "little")]
#[test]
fn record_reading() {
use ndarray_npy::npy::RecordFromSlice;

#[derive(PartialEq, Debug)]
struct Record {
x: i32,
y: i32,
}

impl RecordFromSlice for Record {
fn from_raw_slice<R: std::io::Read>(
reader: &mut R,
) -> Result<Self, ndarray_npy::ReadDataError> {
let mut buf = [0u8; 4];

reader
.read_exact(&mut buf)
.map_err(ndarray_npy::ReadDataError::Io)?;
let x = i32::from_le_bytes(buf);
reader
.read_exact(&mut buf)
.map_err(ndarray_npy::ReadDataError::Io)?;
let y = i32::from_le_bytes(buf);
Ok(Record { x, y })
}

// NOTE: partial validation
fn compatible_schema(type_descr: &py_literal::Value) -> bool {
match type_descr {
py_literal::Value::List(values) => matches!(
&values[..],
// 2 Values per record
[py_literal::Value::Tuple(..), py_literal::Value::Tuple(..)]
),
_ => false,
}
}
}

// np.save("example_xy_little_endian_record", np.rec.array([(42, 42), (35, 35)], dtype=[('x', np.int32), ('y', np.int32)]))
let record_array: Array1<Record> =
ndarray_npy::read_npy("resources/example_xy_little_endian_record.npy")
.expect("Failed to load npy");

assert_eq!(record_array[0], Record { x: 42, y: 42 });
assert_eq!(record_array[1], Record { x: 35, y: 35 });
assert_eq!(record_array.len(), 2);

// np.save("example_xy_little_endian_record_2d.npy", np.rec.array([[(42, 42), (35, 35)]], dtype=[('x', np.int32), ('y', np.int32)]))
let record_array: Array2<Record> =
ndarray_npy::read_npy("resources/example_xy_little_endian_record_2d.npy")
.expect("Failed to load npy");

assert_eq!(record_array[[0, 0]], Record { x: 42, y: 42 });
assert_eq!(record_array[[0, 1]], Record { x: 35, y: 35 });
assert_eq!(record_array.len_of(Axis(0)), 1);
assert_eq!(record_array.len_of(Axis(1)), 2);
}