use std::io;
use crate::{
low::v7400::{ArrayAttributeHeader, AttributeType, SpecialAttributeHeader},
pull_parser::{
error::DataError,
v7400::{FromReader, Parser},
ParserSource, Result, SyntacticPosition, Warning,
},
};
use self::array::{ArrayAttributeValues, AttributeStreamDecoder, BooleanArrayAttributeValues};
pub use self::loader::LoadAttribute;
#[deprecated(
since = "0.4.0",
note = "`DirectAttributeValue` is moved to `low::v7400::AttributeValue`"
)]
pub type DirectAttributeValue = crate::low::v7400::AttributeValue;
mod array;
pub mod iter;
mod loader;
pub mod loaders;
#[derive(Debug)]
pub struct Attributes<'a, R> {
total_count: u64,
rest_count: u64,
next_attr_start_offset: u64,
parser: &'a mut Parser<R>,
}
impl<'a, R: 'a + ParserSource> Attributes<'a, R> {
pub(crate) fn from_parser(parser: &'a mut Parser<R>) -> Self {
let total_count = parser.current_attributes_count();
let pos = parser.reader().position();
Self {
total_count,
rest_count: total_count,
next_attr_start_offset: pos,
parser,
}
}
pub fn total_count(&self) -> u64 {
self.total_count
}
pub fn rest_count(&self) -> u64 {
self.rest_count
}
fn update_next_attr_start_offset(&mut self, size: u64) {
self.next_attr_start_offset = self
.parser
.reader()
.position()
.checked_add(size)
.expect("FBX data too large");
}
pub(crate) fn do_with_health_check<T, F>(&mut self, f: F) -> Result<T>
where
F: FnOnce(&mut Self, u64, usize) -> Result<T>,
{
self.parser.ensure_continuable()?;
let start_pos = self.next_attr_start_offset;
let attr_index = (self.total_count - self.rest_count) as usize;
match f(self, start_pos, attr_index) {
Ok(v) => Ok(v),
Err(e) => {
let err_pos = self.position(start_pos, attr_index);
self.parser.set_aborted(err_pos.clone());
Err(e.and_position(err_pos))
}
}
}
fn read_next_attr_type(&mut self) -> Result<Option<AttributeType>> {
if self.rest_count() == 0 {
return Ok(None);
}
if self.parser.reader().position() < self.next_attr_start_offset {
self.parser.reader().skip_to(self.next_attr_start_offset)?;
}
let attr_type = self.parser.parse::<AttributeType>()?;
self.rest_count -= 1;
Ok(Some(attr_type))
}
pub fn load_next<V>(&mut self, loader: V) -> Result<Option<V::Output>>
where
V: LoadAttribute,
{
self.do_with_health_check(|this, start_pos, attr_index| {
let attr_type = match this.read_next_attr_type()? {
Some(v) => v,
None => return Ok(None),
};
this.load_next_impl(attr_type, loader, start_pos, attr_index)
.map(Some)
})
}
pub fn load_next_buffered<V>(&mut self, loader: V) -> Result<Option<V::Output>>
where
R: io::BufRead,
V: LoadAttribute,
{
self.do_with_health_check(|this, start_pos, attr_index| {
let attr_type = match this.read_next_attr_type()? {
Some(v) => v,
None => return Ok(None),
};
this.load_next_buffered_impl(attr_type, loader, start_pos, attr_index)
.map(Some)
})
}
fn load_next_impl<V>(
&mut self,
attr_type: AttributeType,
loader: V,
start_pos: u64,
attr_index: usize,
) -> Result<V::Output>
where
V: LoadAttribute,
{
match attr_type {
AttributeType::Bool => {
let raw = self.parser.parse::<u8>()?;
let value = (raw & 1) != 0;
self.update_next_attr_start_offset(0);
if raw != b'T' && raw != b'Y' {
self.parser.warn(
Warning::IncorrectBooleanRepresentation,
self.position(start_pos, attr_index),
)?;
}
loader.load_bool(value)
}
AttributeType::I16 => {
let value = self.parser.parse::<i16>()?;
self.update_next_attr_start_offset(0);
loader.load_i16(value)
}
AttributeType::I32 => {
let value = self.parser.parse::<i32>()?;
self.update_next_attr_start_offset(0);
loader.load_i32(value)
}
AttributeType::I64 => {
let value = self.parser.parse::<i64>()?;
self.update_next_attr_start_offset(0);
loader.load_i64(value)
}
AttributeType::F32 => {
let value = self.parser.parse::<f32>()?;
self.update_next_attr_start_offset(0);
loader.load_f32(value)
}
AttributeType::F64 => {
let value = self.parser.parse::<f64>()?;
self.update_next_attr_start_offset(0);
loader.load_f64(value)
}
AttributeType::ArrBool => {
let header = ArrayAttributeHeader::from_reader(self.parser.reader())?;
self.update_next_attr_start_offset(u64::from(header.bytelen));
let reader = AttributeStreamDecoder::create(header.encoding, self.parser.reader())?;
let count = header.elements_count;
let mut iter = BooleanArrayAttributeValues::new(reader, count);
let res = loader.load_seq_bool(&mut iter, count as usize)?;
let has_error = iter.has_error();
if iter.has_incorrect_boolean_value() {
self.parser.warn(
Warning::IncorrectBooleanRepresentation,
self.position(start_pos, attr_index),
)?;
}
if has_error {
return Err(DataError::NodeAttributeError.into());
}
Ok(res)
}
AttributeType::ArrI32 => {
let header = ArrayAttributeHeader::from_reader(self.parser.reader())?;
self.update_next_attr_start_offset(u64::from(header.bytelen));
let reader = AttributeStreamDecoder::create(header.encoding, self.parser.reader())?;
let count = header.elements_count;
let mut iter = ArrayAttributeValues::<_, i32>::new(reader, count);
let res = loader.load_seq_i32(&mut iter, count as usize)?;
if iter.has_error() {
return Err(DataError::NodeAttributeError.into());
}
Ok(res)
}
AttributeType::ArrI64 => {
let header = ArrayAttributeHeader::from_reader(self.parser.reader())?;
self.update_next_attr_start_offset(u64::from(header.bytelen));
let reader = AttributeStreamDecoder::create(header.encoding, self.parser.reader())?;
let count = header.elements_count;
let mut iter = ArrayAttributeValues::<_, i64>::new(reader, count);
let res = loader.load_seq_i64(&mut iter, count as usize)?;
if iter.has_error() {
return Err(DataError::NodeAttributeError.into());
}
Ok(res)
}
AttributeType::ArrF32 => {
let header = ArrayAttributeHeader::from_reader(self.parser.reader())?;
self.update_next_attr_start_offset(u64::from(header.bytelen));
let reader = AttributeStreamDecoder::create(header.encoding, self.parser.reader())?;
let count = header.elements_count;
let mut iter = ArrayAttributeValues::<_, f32>::new(reader, count);
let res = loader.load_seq_f32(&mut iter, count as usize)?;
if iter.has_error() {
return Err(DataError::NodeAttributeError.into());
}
Ok(res)
}
AttributeType::ArrF64 => {
let header = ArrayAttributeHeader::from_reader(self.parser.reader())?;
self.update_next_attr_start_offset(u64::from(header.bytelen));
let reader = AttributeStreamDecoder::create(header.encoding, self.parser.reader())?;
let count = header.elements_count;
let mut iter = ArrayAttributeValues::<_, f64>::new(reader, count);
let res = loader.load_seq_f64(&mut iter, count as usize)?;
if iter.has_error() {
return Err(DataError::NodeAttributeError.into());
}
Ok(res)
}
AttributeType::Binary => {
let header = self.parser.parse::<SpecialAttributeHeader>()?;
let bytelen = u64::from(header.bytelen);
self.update_next_attr_start_offset(bytelen);
let reader = io::Read::take(self.parser.reader(), bytelen);
loader.load_binary(reader, bytelen)
}
AttributeType::String => {
let header = self.parser.parse::<SpecialAttributeHeader>()?;
let bytelen = u64::from(header.bytelen);
self.update_next_attr_start_offset(bytelen);
let reader = io::Read::take(self.parser.reader(), bytelen);
loader.load_string(reader, bytelen)
}
}
}
fn load_next_buffered_impl<V>(
&mut self,
attr_type: AttributeType,
loader: V,
start_pos: u64,
attr_index: usize,
) -> Result<V::Output>
where
R: io::BufRead,
V: LoadAttribute,
{
match attr_type {
AttributeType::Binary => {
let header = self.parser.parse::<SpecialAttributeHeader>()?;
let bytelen = u64::from(header.bytelen);
self.update_next_attr_start_offset(bytelen);
let reader = io::Read::take(self.parser.reader(), bytelen);
loader.load_binary_buffered(reader, bytelen)
}
AttributeType::String => {
let header = self.parser.parse::<SpecialAttributeHeader>()?;
let bytelen = u64::from(header.bytelen);
self.update_next_attr_start_offset(bytelen);
let reader = io::Read::take(self.parser.reader(), bytelen);
loader.load_string_buffered(reader, bytelen)
}
_ => self.load_next_impl(attr_type, loader, start_pos, attr_index),
}
}
fn position(&self, start_pos: u64, index: usize) -> SyntacticPosition {
SyntacticPosition {
component_byte_pos: start_pos,
attribute_index: Some(index),
..self.parser.position()
}
}
pub fn iter<V, I>(&mut self, loaders: I) -> iter::BorrowedIter<'_, 'a, R, I::IntoIter>
where
V: LoadAttribute,
I: IntoIterator<Item = V>,
{
iter::BorrowedIter::new(self, loaders.into_iter())
}
pub fn iter_buffered<V, I>(
&mut self,
loaders: I,
) -> iter::BorrowedIterBuffered<'_, 'a, R, I::IntoIter>
where
R: io::BufRead,
V: LoadAttribute,
I: IntoIterator<Item = V>,
{
iter::BorrowedIterBuffered::new(self, loaders.into_iter())
}
pub fn into_iter<V, I>(self, loaders: I) -> iter::OwnedIter<'a, R, I::IntoIter>
where
V: LoadAttribute,
I: IntoIterator<Item = V>,
{
iter::OwnedIter::new(self, loaders.into_iter())
}
pub fn into_iter_buffered<V, I>(self, loaders: I) -> iter::OwnedIterBuffered<'a, R, I::IntoIter>
where
R: io::BufRead,
V: LoadAttribute,
I: IntoIterator<Item = V>,
{
iter::OwnedIterBuffered::new(self, loaders.into_iter())
}
}