Skip to main content

mlt_core/frames/v01/id/
encode.rs

1use crate::MltResult;
2use crate::codecs::bytes::encode_bools_to_bytes;
3use crate::codecs::rle::encode_byte_rle;
4use crate::v01::{
5    EncodedId, EncodedIdValue, EncodedStream, EncodedStreamData, IdValues, IdWidth, IntEncoder,
6    IntEncoding, LogicalEncoder, LogicalEncoding, PhysicalEncoder, PhysicalEncoding, RleMeta,
7    StreamMeta, StreamType,
8};
9
10/// How to encode IDs
11#[derive(Debug, Clone, Copy, PartialEq)]
12#[cfg_attr(all(not(test), feature = "arbitrary"), derive(arbitrary::Arbitrary))]
13pub struct IdEncoder {
14    pub logical: LogicalEncoder,
15    pub id_width: IdWidth,
16}
17
18impl IdEncoder {
19    #[must_use]
20    pub fn new(logical: LogicalEncoder, id_width: IdWidth) -> Self {
21        Self { logical, id_width }
22    }
23}
24
25impl EncodedId {
26    pub(crate) fn encode(value: &IdValues, encoder: IdEncoder) -> MltResult<Self> {
27        use IdWidth as CFG;
28
29        let presence = if matches!(encoder.id_width, CFG::OptId32 | CFG::OptId64) {
30            let present: Vec<bool> = value.0.iter().map(Option::is_some).collect();
31            let num_values = u32::try_from(present.len())?;
32            let data = encode_byte_rle(&encode_bools_to_bytes(&present));
33
34            // Presence streams always use byte-RLE encoding.
35            // The RleMeta values are computed by readers from the stream itself
36            // (runs = num_values.div_ceil(8), num_rle_values = byte_length).
37            let runs = num_values.div_ceil(8);
38            let num_rle_values = u32::try_from(data.len())?;
39            let meta = StreamMeta::new(
40                StreamType::Present,
41                IntEncoding::new(
42                    LogicalEncoding::Rle(RleMeta {
43                        runs,
44                        num_rle_values,
45                    }),
46                    PhysicalEncoding::None,
47                ),
48                num_values,
49            );
50
51            Some(EncodedStream {
52                meta,
53                data: EncodedStreamData::Encoded(data),
54            })
55        } else {
56            None
57        };
58
59        let value_stream = if matches!(encoder.id_width, CFG::Id32 | CFG::OptId32) {
60            #[expect(clippy::cast_possible_truncation, reason = "truncation was requested")]
61            let vals: Vec<u32> = value
62                .0
63                .iter()
64                .filter_map(|&id| id)
65                .map(|v| v as u32)
66                .collect();
67            EncodedIdValue::Id32(EncodedStream::encode_u32s(
68                &vals,
69                IntEncoder::new(encoder.logical, PhysicalEncoder::VarInt),
70            )?)
71        } else {
72            let vals: Vec<u64> = value.0.iter().filter_map(|&id| id).collect();
73            EncodedIdValue::Id64(EncodedStream::encode_u64s(
74                &vals,
75                IntEncoder::new(encoder.logical, PhysicalEncoder::VarInt),
76            )?)
77        };
78
79        Ok(Self {
80            presence,
81            value: value_stream,
82        })
83    }
84}