fn read_ipma()

in mp4parse/src/lib.rs [3513:3592]


fn read_ipma<T: Read>(
    src: &mut BMFFBox<T>,
    strictness: ParseStrictness,
    version: u8,
    flags: u32,
) -> Result<TryVec<ItemPropertyAssociationEntry>> {
    let entry_count = be_u32(src)?;
    let num_association_bytes =
        std::num::NonZeroU8::new(if flags & 1 == 1 { 2 } else { 1 }).unwrap();

    let total_associations = calculate_ipma_total_associations(
        version,
        src.bytes_left(),
        U32::new(entry_count),
        num_association_bytes,
    )?;
    // Assuming most items will have at least `MIN_PROPERTIES` and knowing the
    // total number of item -> property associations (`total_associations`),
    // we can provide a good estimate for how many elements we'll need in this
    // vector, even though we don't know precisely how many items there will be
    // properties for.
    let mut entries = TryVec::<ItemPropertyAssociationEntry>::with_capacity(
        total_associations / ItemPropertiesBox::MIN_PROPERTIES,
    )?;

    for _ in 0..entry_count {
        let item_id = ItemId::read(src, version)?;

        if let Some(previous_association) = entries.last() {
            #[allow(clippy::comparison_chain)]
            if previous_association.item_id > item_id {
                return Status::IpmaBadItemOrder.into();
            } else if previous_association.item_id == item_id {
                return Status::IpmaDuplicateItemId.into();
            }
        }

        let association_count = src.read_u8()?;
        let mut associations = TryVec::with_capacity(association_count.to_usize())?;
        for _ in 0..association_count {
            let association = src
                .take(num_association_bytes.get().into())
                .read_into_try_vec()?;
            let mut association = BitReader::new(association.as_slice());
            let essential = association.read_bool()?;
            let property_index =
                PropertyIndex(association.read_u16(association.remaining().try_into()?)?);
            associations.push(Association {
                essential,
                property_index,
            })?;
        }

        entries.push(ItemPropertyAssociationEntry {
            item_id,
            associations,
        })?;
    }

    check_parser_state!(src.content);

    if version != 0 {
        if let Some(ItemPropertyAssociationEntry {
            item_id: max_item_id,
            ..
        }) = entries.last()
        {
            if *max_item_id <= ItemId(u16::MAX.into()) {
                fail_with_status_if(
                    strictness == ParseStrictness::Strict,
                    Status::IpmaBadVersion,
                )?;
            }
        }
    }

    trace!("read_ipma -> {:#?}", entries);

    Ok(entries)
}