Skip to main content

Matrix

Struct Matrix 

Source
pub struct Matrix<T> { /* private fields */ }
Expand description

An owned matrix: a layout, the length of each dimension, and the elements in that order.

use structio::{Matrix, MatrixLayout};

let m = Matrix::new(MatrixLayout::RowMajor, vec![2, 2], vec![1.0f64, 2.0, 3.0, 4.0]).unwrap();
assert_eq!(m.extents(), &[2, 2]);

let bytes = structio::to_beve(&m);
assert_eq!(structio::from_beve::<Matrix<f64>>(&bytes).unwrap(), m);
assert_eq!(
    structio::beve_to_json(&bytes).unwrap(),
    r#"{"layout":"layout_right","extents":[2,2],"value":[1,2,3,4]}"#
);

The elements are one flat run, since that is what the format stores and what makes storing it cheap. Indexing into them is arithmetic this type deliberately does not do: which of the two layouts is in force changes the answer, and a program that computes with matrices already has a type that knows. Use Matrix::into_parts to hand the pieces to it.

Implementations§

Source§

impl<T> Matrix<T>

Source

pub fn new( layout: MatrixLayout, extents: Vec<usize>, data: Vec<T>, ) -> Result<Self, ErrorCode>

Build a matrix, checking that the extents describe the data.

The only failure is InvalidMatrixShape, and it is checked here so that it can never be checked again: nothing downstream of this, writing included, has to consider a matrix whose halves disagree.

Examples found in repository?
examples/beve.rs (line 267)
95fn main() -> Result<(), Box<dyn std::error::Error>> {
96    let value = run();
97
98    // -- The same value, either way ------------------------------------------
99    let text = structio::to_string(&value);
100    let binary = structio::to_beve(&value);
101
102    assert_eq!(structio::from_str::<Run>(&text)?, value);
103    assert_eq!(structio::from_beve::<Run>(&binary)?, value);
104
105    println!("json  {:>6} bytes", text.len());
106    println!(
107        "beve  {:>6} bytes  ({:.0}% of the text)",
108        binary.len(),
109        100.0 * binary.len() as f64 / text.len() as f64
110    );
111    // The thousand samples are 8 bytes each and nothing more; the thousand
112    // booleans are one bit each.
113    println!(
114        "      {} of those bytes are the sample payload, {} the flags",
115        value.samples.len() * 8,
116        value.valid.len().div_ceil(8)
117    );
118
119    // -- Reading into a value you already have -------------------------------
120    // The destination keeps its buffers, so a loop over many documents of the
121    // same shape settles into doing no allocation at all.
122    let mut into = Run::default();
123    structio::read_beve_into(&mut into, &binary)?;
124    let at = into.samples.as_ptr();
125    structio::read_beve_into(&mut into, &binary)?;
126    assert_eq!(into.samples.as_ptr(), at, "the allocation was reused");
127
128    // -- Borrowing straight out of the buffer --------------------------------
129    let frame = Frame {
130        id: 9,
131        payload: &[0xDE, 0xAD, 0xBE, 0xEF],
132        note: "no copies here",
133    };
134    let bytes = structio::to_beve(&frame);
135    let back: Frame = structio::from_beve(&bytes)?;
136    assert_eq!(back, frame);
137    let inside = (back.note.as_ptr() as usize) - (bytes.as_ptr() as usize) < bytes.len();
138    println!("borrowed fields point into the input: {inside}");
139
140    // -- Arrays a reader can point at ----------------------------------------
141    // BEVE's aligned form pads each numeric payload onto its own element
142    // width, so a reader with an aligned buffer can borrow the block where it
143    // lies instead of copying it out. The same document otherwise: this one
144    // still reads back into the same value.
145    assert_eq!(
146        structio::from_beve::<Run>(&structio::to_beve_aligned(&value))?,
147        value
148    );
149    let padded = structio::to_beve_aligned(&value.samples);
150    let payload = padded.len() - value.samples.len() * 8;
151    assert_eq!(payload % 8, 0);
152    println!(
153        "the aligned form puts {} samples at byte {payload}",
154        value.samples.len()
155    );
156
157    // And a reader takes that offer up: `try_slice` hands the block back as a
158    // `&[f64]` pointing into the document itself. Whether it can is partly the
159    // allocator's decision, the document having to sit on an address an `f64`
160    // could live at, so the answer is an `Option` rather than a promise, and
161    // `Cow<[f64]>` is the field type that copies when it has to.
162    let mut reader = structio::beve::Reader::new(&padded);
163    match reader.try_slice::<f64>() {
164        Some(block) => println!("borrowed {} samples with no copy at all", block.len()),
165        None => println!("this buffer is not one a &[f64] can point into"),
166    }
167
168    // -- Writing to a sink ---------------------------------------------------
169    // Drained as it is produced, so peak memory is the buffer rather than the
170    // size of the output.
171    let mut file = Vec::new();
172    structio::to_beve_writer(&value, &mut file)?;
173    assert_eq!(file, binary);
174
175    // -- Length-prefixed frames ----------------------------------------------
176    let runs = [run(), run()];
177    let mut sink = Vec::new();
178    send_frames(&runs, &mut sink)?;
179
180    // The unbuffered form is the same stream, byte for byte, which is the
181    // whole claim `beve_size` makes.
182    let mut streamed = Vec::new();
183    stream_frames(&runs, &mut streamed)?;
184    assert_eq!(streamed, sink);
185
186    let mut rest = &sink[..];
187    for want in &runs {
188        let (len, tail) = rest.split_at(4);
189        let len = u32::from_le_bytes(len.try_into()?) as usize;
190        let (frame, tail) = tail.split_at(len);
191        assert_eq!(&structio::from_beve::<Run>(frame)?, want);
192        rest = tail;
193    }
194    assert!(rest.is_empty());
195    println!("read back {} length-prefixed frames", runs.len());
196
197    // A body behind a header, in the aligned form. The payload lands on its
198    // element width counted from the start of the frame, whatever the query in
199    // front of it is, and the stated length is the body's own.
200    for query in ["", "/sensor", "/a/rather/longer/route"] {
201        let frame = frame_aligned(query, &value.samples);
202        let base = 8 + query.len();
203        let stated = u64::from_le_bytes(frame[..8].try_into()?) as usize;
204        assert_eq!(frame.len() - base, stated);
205        assert_eq!((frame.len() - value.samples.len() * 8) % 8, 0);
206        assert_eq!(
207            structio::from_beve::<Vec<f64>>(&frame[base..])?,
208            value.samples
209        );
210    }
211    println!("aligned bodies stay aligned behind a header of any length");
212
213    // -- Looking at a document you have no type for --------------------------
214    // No schema involved: the binary states every value's kind and extent, so
215    // the walk that reads it drives the JSON writer directly. What comes out is
216    // the same bytes the typed path above produced.
217    assert_eq!(structio::beve_to_json(&binary)?, text);
218    let unknown = structio::beve_to_json(&structio::to_beve(&frame))?;
219    println!("a document with no declared type: {unknown}");
220
221    // -- A file too large to hold --------------------------------------------
222    // `from_beve` wants the whole document. `Documents` wants one value of it,
223    // so the cost of a million records is one record, not a million. A typed
224    // array streams too: the elements carry no headers, and the one the array
225    // implied is supplied to the reader with each span.
226    let archive = structio::to_beve(&vec![run(), run(), run()]);
227    let mut docs = structio::beve::Documents::array(&archive[..]).read_size(16);
228    // Into an existing value, so the loop stops allocating after the first
229    // record as well as holding only one at a time.
230    let mut record = Run::default();
231    let mut count = 0;
232    while let Some(result) = docs.next_value_into(&mut record) {
233        result?;
234        count += 1;
235    }
236    println!(
237        "read {count} records out of {} bytes without ever holding the file",
238        archive.len()
239    );
240
241    // -- What BEVE carries that JSON cannot ----------------------------------
242    let odd = vec![f64::NAN, f64::INFINITY, -0.0];
243    let back: Vec<f64> = structio::from_beve(&structio::to_beve(&odd))?;
244    assert!(back[0].is_nan() && back[1].is_infinite() && back[2].is_sign_negative());
245    println!("NaN, infinity, and negative zero survive the round trip");
246
247    // -- Complex numbers and matrices ----------------------------------------
248    // BEVE's two data-carrying extensions have types, and both work in JSON as
249    // well, so a struct holding one still declares its schema once.
250    let signal = vec![
251        structio::Complex::new(1.0f64, 2.0),
252        structio::Complex::new(3.0, -4.0),
253    ];
254    let bytes = structio::to_beve(&signal);
255    println!(
256        "{} complex samples in {} bytes: one header, one count, and the components",
257        signal.len(),
258        bytes.len()
259    );
260    assert_eq!(
261        structio::from_beve::<Vec<structio::Complex<f64>>>(&bytes)?,
262        signal
263    );
264
265    // A matrix stores its data as an ordinary value, so a matrix of complex
266    // numbers is the run above with a shape in front of it.
267    let grid = structio::Matrix::new(structio::MatrixLayout::RowMajor, vec![1, 2], signal.clone())?;
268    let bytes = structio::to_beve(&grid);
269    assert_eq!(
270        structio::from_beve::<structio::Matrix<structio::Complex<f64>>>(&bytes)?,
271        grid
272    );
273    println!("as a matrix: {}", structio::beve_to_json(&bytes)?);
274
275    // -- Fields you do not know about ----------------------------------------
276    // A key no field claims is refused by default, which is what catches a
277    // typo or the wrong document. `SkipUnknown` asks for the other behaviour:
278    // take the members you recognize and step over the rest, so a producer can
279    // add fields without breaking you.
280    #[derive(Default, Debug, PartialEq)]
281    struct JustTheLabel {
282        label: String,
283    }
284    structio::object!(JustTheLabel { label });
285
286    let partial = structio::from_beve_with::<structio::SkipUnknown, JustTheLabel>(&binary)?;
287    assert_eq!(partial.label, value.label);
288    println!("unknown members are stepped over: {:?}", partial.label);
289
290    Ok(())
291}
Source

pub fn layout(&self) -> MatrixLayout

Source

pub fn set_layout(&mut self, layout: MatrixLayout)

Reinterpret the same elements in the other storage order.

Nothing moves: the layout says how the elements the matrix already holds are to be read, so changing it changes what they mean.

Source

pub fn extents(&self) -> &[usize]

The length of each dimension, outermost first.

Source

pub fn rank(&self) -> usize

How many dimensions the matrix has.

Source

pub fn data(&self) -> &[T]

Source

pub fn data_mut(&mut self) -> &mut [T]

The elements, mutably.

A slice rather than the Vec, which is what keeps the shape true: the values may change and their number may not.

Source

pub fn len(&self) -> usize

Source

pub fn is_empty(&self) -> bool

Source

pub fn into_parts(self) -> (MatrixLayout, Vec<usize>, Vec<T>)

Take the three pieces apart, which is where a matrix stops being this crate’s problem and starts being your linear algebra library’s.

Trait Implementations§

Source§

impl<T: Clone> Clone for Matrix<T>

Source§

fn clone(&self) -> Matrix<T>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug> Debug for Matrix<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Default> Default for Matrix<T>

Source§

fn default() -> Matrix<T>

Returns the “default value” for a type. Read more
Source§

impl<T: Eq> Eq for Matrix<T>

Source§

impl<T: Hash> Hash for Matrix<T>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T: PartialEq> PartialEq for Matrix<T>

Source§

fn eq(&self, other: &Matrix<T>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<'de, T> Read<'de> for Matrix<T>
where T: Read<'de> + Default,

Source§

fn read<O: Options>(&mut self, r: &mut Reader<'de, O>) -> Result<(), ErrorCode>

Read into self, from the reader’s current position. Read more
Source§

fn read_bulk<O: Options>( _out: &mut Vec<Self>, _n: usize, _elem: u8, _r: &mut Reader<'de, O>, ) -> Result<bool, ErrorCode>

Fill out from the payload of a typed array of n elements whose element header is elem, or return false to decline. Read more
Source§

impl<'de, T> Read<'de> for Matrix<T>
where T: Read<'de> + Default,

Source§

fn read<O: Options>(&mut self, p: &mut Parser<'de, O>) -> Result<(), ErrorCode>

Parse into self, from the cursor’s current position. Read more
Source§

impl<T: PartialEq> StructuralPartialEq for Matrix<T>

Source§

impl<T: Write> Write for Matrix<T>

Source§

fn write<O: Options>(&self, w: &mut Writer<'_, O>)

Source§

fn is_null(&self) -> bool

Whether this value is absent, and so is left out of an object under Options::SKIP_NULL. Read more
Source§

const ARRAY: Option<&'static [u8]> = None

The header bytes a contiguous run of this type is stored under, or None if it has no array form of its own and belongs in a generic one. Read more
Source§

fn write_payload<O: Options>(items: &[Self], w: &mut Writer<'_, O>)
where Self: Sized,

Append items as the payload of a typed array whose header and count the caller has already written. Read more
Source§

impl<T: Write> Write for Matrix<T>

Source§

fn write<O: Options>(&self, w: &mut Writer<'_, O>)

Source§

fn is_null(&self) -> bool

Whether this value is absent, and so is left out of an object under Options::SKIP_NULL. Read more

Auto Trait Implementations§

§

impl<T> Freeze for Matrix<T>
where Vec<T>: Freeze,

§

impl<T> RefUnwindSafe for Matrix<T>
where Vec<T>: RefUnwindSafe,

§

impl<T> Send for Matrix<T>
where Vec<T>: Send,

§

impl<T> Sync for Matrix<T>
where Vec<T>: Sync,

§

impl<T> Unpin for Matrix<T>
where Vec<T>: Unpin,

§

impl<T> UnsafeUnpin for Matrix<T>
where Vec<T>: UnsafeUnpin,

§

impl<T> UnwindSafe for Matrix<T>
where Vec<T>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ReadWrite for T
where T: for<'de> Read<'de> + Write,

Source§

impl<T> ReadWrite for T
where T: for<'de> Read<'de> + Write,

Source§

impl<T> ReadWrite for T
where T: ReadWrite + ReadWrite,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.