use super::PagedRelation;
use crate::{AttributeKind, AttributeValues, ChunkDomainRef, DatasetError, RigidInstance};
use std::sync::Arc;
#[derive(Clone, PartialEq, Debug)]
pub struct PointChunkPayload {
positions: Arc<[[f32; 3]]>,
}
impl PointChunkPayload {
pub fn new(positions: Arc<[[f32; 3]]>) -> Result<Self, DatasetError> {
checked_rows(positions.len())?;
if positions.is_empty() || positions.iter().flatten().any(|value| !value.is_finite()) {
return Err(invalid(
"point chunk positions must be non-empty and finite",
));
}
Ok(Self { positions })
}
#[must_use]
pub const fn positions(&self) -> &Arc<[[f32; 3]]> {
&self.positions
}
pub(in crate::dataset) fn row_count(&self) -> Result<u32, DatasetError> {
checked_rows(self.positions.len())
}
pub(in crate::dataset) fn minimum_host_bytes(&self) -> Result<u64, DatasetError> {
bytes(self.positions.len(), 12)
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct InstanceChunkPayload {
transforms: Arc<[RigidInstance]>,
}
impl InstanceChunkPayload {
pub fn new(transforms: Arc<[RigidInstance]>) -> Result<Self, DatasetError> {
checked_rows(transforms.len())?;
if transforms.is_empty() {
return Err(invalid("instance chunk transforms must be non-empty"));
}
Ok(Self { transforms })
}
#[must_use]
pub const fn transforms(&self) -> &Arc<[RigidInstance]> {
&self.transforms
}
pub(in crate::dataset) fn row_count(&self) -> Result<u32, DatasetError> {
checked_rows(self.transforms.len())
}
pub(in crate::dataset) fn minimum_host_bytes(&self) -> Result<u64, DatasetError> {
bytes(self.transforms.len(), 32)
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct RelationChunkPayload {
relations: Arc<[PagedRelation]>,
}
impl RelationChunkPayload {
pub fn new(relations: Arc<[PagedRelation]>) -> Result<Self, DatasetError> {
checked_rows(relations.len())?;
if relations.is_empty()
|| relations
.iter()
.flat_map(|relation| [relation.start, relation.end])
.any(|anchor| !anchor.valid())
{
return Err(invalid("relation chunk anchors must be spatial and valid"));
}
Ok(Self { relations })
}
#[must_use]
pub const fn relations(&self) -> &Arc<[PagedRelation]> {
&self.relations
}
pub(in crate::dataset) fn row_count(&self) -> Result<u32, DatasetError> {
checked_rows(self.relations.len())
}
pub(in crate::dataset) fn minimum_host_bytes(&self) -> Result<u64, DatasetError> {
bytes(
self.relations.len(),
std::mem::size_of::<PagedRelation>() as u64,
)
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AttributeChunkPayload {
target: ChunkDomainRef,
values: AttributeValues,
}
impl AttributeChunkPayload {
pub fn new(target: ChunkDomainRef, values: AttributeValues) -> Result<Self, DatasetError> {
checked_rows(values.len())?;
if values.is_empty() || values.validate().is_err() {
return Err(invalid(
"attribute chunk values must be non-empty and valid",
));
}
Ok(Self { target, values })
}
#[must_use]
pub const fn target(&self) -> ChunkDomainRef {
self.target
}
#[must_use]
pub const fn kind(&self) -> AttributeKind {
self.values.kind()
}
#[must_use]
pub const fn values(&self) -> &AttributeValues {
&self.values
}
pub(in crate::dataset) fn row_count(&self) -> Result<u32, DatasetError> {
checked_rows(self.values.len())
}
pub(in crate::dataset) fn minimum_host_bytes(&self) -> Result<u64, DatasetError> {
bytes(self.values.len(), u64::from(self.values.kind().stride()))
}
}
fn checked_rows(rows: usize) -> Result<u32, DatasetError> {
u32::try_from(rows).map_err(|_| invalid("generic chunk exceeds local u32 addressing"))
}
fn bytes(rows: usize, stride: u64) -> Result<u64, DatasetError> {
u64::try_from(rows)
.ok()
.and_then(|rows| rows.checked_mul(stride))
.ok_or(DatasetError::PayloadByteSizeOverflow)
}
const fn invalid(reason: &'static str) -> DatasetError {
DatasetError::InvalidPayload { reason }
}