use crate::beve;
use crate::beve::header;
use crate::error::{ErrorCode, PResult};
use crate::json;
use crate::options::Options;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum MatrixLayout {
#[default]
RowMajor,
ColumnMajor,
}
impl MatrixLayout {
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
MatrixLayout::RowMajor => "layout_right",
MatrixLayout::ColumnMajor => "layout_left",
}
}
#[inline]
pub(crate) const fn as_byte(self) -> u8 {
match self {
MatrixLayout::RowMajor => header::LAYOUT_RIGHT,
MatrixLayout::ColumnMajor => header::LAYOUT_LEFT,
}
}
#[inline]
pub(crate) const fn from_byte(b: u8) -> Option<Self> {
match b {
header::LAYOUT_RIGHT => Some(MatrixLayout::RowMajor),
header::LAYOUT_LEFT => Some(MatrixLayout::ColumnMajor),
_ => None,
}
}
}
impl core::str::FromStr for MatrixLayout {
type Err = ErrorCode;
fn from_str(s: &str) -> Result<Self, ErrorCode> {
match s {
"layout_right" | "row_major" | "right" => Ok(MatrixLayout::RowMajor),
"layout_left" | "column_major" | "left" => Ok(MatrixLayout::ColumnMajor),
_ => Err(ErrorCode::InvalidMatrixLayout),
}
}
}
fn element_count(extents: &[usize]) -> Option<usize> {
if extents.is_empty() {
return Some(0);
}
extents
.iter()
.copied()
.try_fold(1usize, |acc, e| acc.checked_mul(e))
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Matrix<T> {
layout: MatrixLayout,
extents: Vec<usize>,
data: Vec<T>,
}
impl<T> Matrix<T> {
pub fn new(layout: MatrixLayout, extents: Vec<usize>, data: Vec<T>) -> Result<Self, ErrorCode> {
if element_count(&extents) != Some(data.len()) {
return Err(ErrorCode::InvalidMatrixShape);
}
Ok(Matrix {
layout,
extents,
data,
})
}
#[inline]
pub fn layout(&self) -> MatrixLayout {
self.layout
}
#[inline]
pub fn set_layout(&mut self, layout: MatrixLayout) {
self.layout = layout;
}
#[inline]
pub fn extents(&self) -> &[usize] {
&self.extents
}
#[inline]
pub fn rank(&self) -> usize {
self.extents.len()
}
#[inline]
pub fn data(&self) -> &[T] {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut [T] {
&mut self.data
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline]
pub fn into_parts(self) -> (MatrixLayout, Vec<usize>, Vec<T>) {
(self.layout, self.extents, self.data)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MatrixRef<'a, T> {
layout: MatrixLayout,
extents: &'a [usize],
data: &'a [T],
}
impl<'a, T> MatrixRef<'a, T> {
pub fn new(
layout: MatrixLayout,
extents: &'a [usize],
data: &'a [T],
) -> Result<Self, ErrorCode> {
if element_count(extents) != Some(data.len()) {
return Err(ErrorCode::InvalidMatrixShape);
}
Ok(MatrixRef {
layout,
extents,
data,
})
}
pub fn to_matrix(self) -> Matrix<T>
where
T: Clone,
{
Matrix {
layout: self.layout,
extents: self.extents.to_vec(),
data: self.data.to_vec(),
}
}
}
const LAYOUT: &str = "layout";
const EXTENTS: &str = "extents";
const VALUE: &str = "value";
pub(crate) const LAYOUT_MEMBER: &str = "\"layout\":";
pub(crate) const EXTENTS_MEMBER: &str = "\"extents\":";
pub(crate) const VALUE_MEMBER: &str = "\"value\":";
const SEEN_LAYOUT: u8 = 1;
const SEEN_EXTENTS: u8 = 2;
const SEEN_VALUE: u8 = 4;
const SEEN_ALL: u8 = SEEN_LAYOUT | SEEN_EXTENTS | SEEN_VALUE;
fn missing_member<O: Options>(seen: u8) -> Option<&'static str> {
if !O::ERROR_ON_MISSING_KEYS || seen == SEEN_ALL {
return None;
}
Some(if seen & SEEN_LAYOUT == 0 {
LAYOUT
} else if seen & SEEN_EXTENTS == 0 {
EXTENTS
} else {
VALUE
})
}
fn write_extents<O: Options>(w: &mut beve::Writer<'_, O>, extents: &[usize]) {
let max = extents.iter().copied().max().unwrap_or(0);
let width = if u8::try_from(max).is_ok() {
1
} else if u16::try_from(max).is_ok() {
2
} else if u32::try_from(max).is_ok() {
4
} else {
8
};
w.begin_typed_array(
header::array_of(header::CAT_UNSIGNED, header::code_for(width)),
extents.len(),
);
for &e in extents {
w.raw(&(e as u64).to_le_bytes()[..width]);
}
}
fn write_beve<O: Options, T: beve::Write>(
layout: MatrixLayout,
extents: &[usize],
data: &[T],
w: &mut beve::Writer<'_, O>,
) {
w.begin_matrix(layout.as_byte());
write_extents(w, extents);
w.write_slice(data);
}
fn commit_shape<T>(m: &mut Matrix<T>, outcome: PResult<()>) -> PResult<()> {
if outcome.is_ok() && element_count(&m.extents) == Some(m.data.len()) {
return Ok(());
}
m.layout = MatrixLayout::default();
m.extents.clear();
m.data.clear();
outcome?;
Err(ErrorCode::InvalidMatrixShape)
}
fn read_beve<'de, O: Options, T>(m: &mut Matrix<T>, r: &mut beve::Reader<'de, O>) -> PResult<()>
where
T: beve::Read<'de> + Default,
{
let outcome = fill_beve(m, r);
commit_shape(m, outcome)
}
fn fill_beve<'de, O: Options, T>(m: &mut Matrix<T>, r: &mut beve::Reader<'de, O>) -> PResult<()>
where
T: beve::Read<'de> + Default,
{
match r.peek() {
Some(header::MATRIX) => {
r.head()?;
m.layout =
MatrixLayout::from_byte(r.take(1)?[0]).ok_or(ErrorCode::InvalidMatrixLayout)?;
r.enter()?;
beve::Read::read(&mut m.extents, r)?;
beve::Read::read(&mut m.data, r)?;
r.leave();
Ok(())
}
Some(h) if header::ty(h) == header::TY_OBJECT => {
let open = r.position();
let mut seen = 0u8;
r.read_map(|r, key| match key {
beve::Key::Str(LAYOUT) => {
seen |= SEEN_LAYOUT;
m.layout = r.read_str()?.parse()?;
Ok(())
}
beve::Key::Str(EXTENTS) => {
seen |= SEEN_EXTENTS;
beve::Read::read(&mut m.extents, r)
}
beve::Key::Str(VALUE) => {
seen |= SEEN_VALUE;
beve::Read::read(&mut m.data, r)
}
_ if O::ERROR_ON_UNKNOWN_KEYS => Err(ErrorCode::UnknownKey),
_ => r.skip_value(),
})?;
match missing_member::<O>(seen) {
None => Ok(()),
Some(key) => {
r.rewind(open);
r.set_error_key(key);
Err(ErrorCode::MissingKey)
}
}
}
Some(_) => Err(ErrorCode::ExpectedMatrix),
None => Err(ErrorCode::UnexpectedEnd),
}
}
impl<'de, T> beve::Read<'de> for Matrix<T>
where
T: beve::Read<'de> + Default,
{
fn read<O: Options>(&mut self, r: &mut beve::Reader<'de, O>) -> PResult<()> {
read_beve(self, r)
}
}
impl<T: beve::Write> beve::Write for Matrix<T> {
#[inline]
fn write<O: Options>(&self, w: &mut beve::Writer<'_, O>) {
write_beve(self.layout, &self.extents, &self.data, w);
}
}
impl<T: beve::Write> beve::Write for MatrixRef<'_, T> {
#[inline]
fn write<O: Options>(&self, w: &mut beve::Writer<'_, O>) {
write_beve(self.layout, self.extents, self.data, w);
}
}
fn write_json<O: Options, T: json::Write>(
layout: MatrixLayout,
extents: &[usize],
data: &[T],
w: &mut json::Writer<'_, O>,
) {
w.open(b'{');
w.member(LAYOUT_MEMBER, layout.as_str());
w.member(EXTENTS_MEMBER, extents);
w.member(VALUE_MEMBER, data);
w.close(b'}');
}
fn read_json<'de, O: Options, T>(m: &mut Matrix<T>, p: &mut json::Parser<'de, O>) -> PResult<()>
where
T: json::Read<'de> + Default,
{
p.skip_ws();
let open = p.position();
let mut seen = 0u8;
let outcome = p.read_map(|p, key| match key.as_str() {
LAYOUT => {
seen |= SEEN_LAYOUT;
let name = p.read_string()?;
m.layout = name.as_str().parse()?;
Ok(())
}
EXTENTS => {
seen |= SEEN_EXTENTS;
json::Read::read(&mut m.extents, p)
}
VALUE => {
seen |= SEEN_VALUE;
json::Read::read(&mut m.data, p)
}
_ if O::ERROR_ON_UNKNOWN_KEYS => Err(ErrorCode::UnknownKey),
_ => p.skip_value(),
});
let outcome = outcome.and_then(|()| match missing_member::<O>(seen) {
None => Ok(()),
Some(key) => {
p.rewind(open);
p.set_error_key(key);
Err(ErrorCode::MissingKey)
}
});
commit_shape(m, outcome)
}
impl<'de, T> json::Read<'de> for Matrix<T>
where
T: json::Read<'de> + Default,
{
fn read<O: Options>(&mut self, p: &mut json::Parser<'de, O>) -> PResult<()> {
read_json(self, p)
}
}
impl<T: json::Write> json::Write for Matrix<T> {
#[inline]
fn write<O: Options>(&self, w: &mut json::Writer<'_, O>) {
write_json(self.layout, &self.extents, &self.data, w);
}
}
impl<T: json::Write> json::Write for MatrixRef<'_, T> {
#[inline]
fn write<O: Options>(&self, w: &mut json::Writer<'_, O>) {
write_json(self.layout, self.extents, self.data, w);
}
}