Skip to main content

draco_core/
metadata.rs

1#[cfg(feature = "decoder")]
2use crate::decoder_buffer::DecoderBuffer;
3#[cfg(feature = "encoder")]
4use crate::encoder_buffer::EncoderBuffer;
5use crate::status::DracoError;
6use std::collections::BTreeMap;
7use std::mem::size_of;
8use std::str;
9
10/// Header flag bit indicating that geometry metadata follows the payload.
11pub const METADATA_FLAG_MASK: u16 = 0x8000;
12
13#[cfg(feature = "decoder")]
14const MAX_METADATA_NESTING_DEPTH: usize = 1000;
15const MAX_METADATA_NAME_LEN: usize = u8::MAX as usize;
16
17/// Raw Draco metadata map.
18///
19/// Entries store the exact byte payload written to the Draco bitstream. Typed
20/// helpers encode values using the same little-endian layout as C++ Draco.
21#[derive(Debug, Clone, Default, PartialEq, Eq)]
22pub struct Metadata {
23    entries: BTreeMap<String, Vec<u8>>,
24    sub_metadata: BTreeMap<String, Metadata>,
25}
26
27/// Metadata associated with one geometry attribute unique id.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct AttributeMetadata {
30    attribute_unique_id: u32,
31    metadata: Metadata,
32}
33
34/// Geometry-level metadata plus per-attribute metadata.
35#[derive(Debug, Clone, Default, PartialEq, Eq)]
36pub struct GeometryMetadata {
37    metadata: Metadata,
38    attribute_metadata: Vec<AttributeMetadata>,
39}
40
41impl Metadata {
42    /// Creates an empty metadata map.
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    /// Returns true when there are no entries or nested metadata blocks.
48    pub fn is_empty(&self) -> bool {
49        self.entries.is_empty() && self.sub_metadata.is_empty()
50    }
51
52    /// Returns all raw metadata entries.
53    pub fn entries(&self) -> &BTreeMap<String, Vec<u8>> {
54        &self.entries
55    }
56
57    /// Returns all nested metadata blocks.
58    pub fn sub_metadata(&self) -> &BTreeMap<String, Metadata> {
59        &self.sub_metadata
60    }
61
62    /// Returns the raw byte value for an entry.
63    pub fn get_raw(&self, name: &str) -> Option<&[u8]> {
64        self.entries.get(name).map(Vec::as_slice)
65    }
66
67    /// Sets a raw byte metadata entry.
68    pub fn set_raw(
69        &mut self,
70        name: impl Into<String>,
71        value: impl Into<Vec<u8>>,
72    ) -> Result<(), DracoError> {
73        let name = name.into();
74        validate_metadata_name(&name)?;
75        let value = value.into();
76        if value.is_empty() {
77            return Err(DracoError::InvalidParameter(
78                "Metadata entry value must not be empty".to_string(),
79            ));
80        }
81        self.entries.insert(name, value);
82        Ok(())
83    }
84
85    /// Stores an `i32` metadata entry.
86    pub fn set_i32(&mut self, name: impl Into<String>, value: i32) -> Result<(), DracoError> {
87        self.set_raw(name, value.to_le_bytes().to_vec())
88    }
89
90    /// Reads an `i32` metadata entry.
91    pub fn get_i32(&self, name: &str) -> Option<i32> {
92        let bytes = self.get_raw(name)?;
93        let bytes: [u8; size_of::<i32>()] = bytes.try_into().ok()?;
94        Some(i32::from_le_bytes(bytes))
95    }
96
97    /// Stores an array of `i32` metadata values.
98    pub fn set_i32_array(
99        &mut self,
100        name: impl Into<String>,
101        values: &[i32],
102    ) -> Result<(), DracoError> {
103        let mut bytes = Vec::new();
104        let byte_len = values
105            .len()
106            .checked_mul(size_of::<i32>())
107            .ok_or_else(|| DracoError::InvalidParameter("Metadata array too large".to_string()))?;
108        bytes
109            .try_reserve_exact(byte_len)
110            .map_err(|_| DracoError::InvalidParameter("Metadata array too large".to_string()))?;
111        for value in values {
112            bytes.extend_from_slice(&value.to_le_bytes());
113        }
114        self.set_raw(name, bytes)
115    }
116
117    /// Reads an array of `i32` metadata values.
118    pub fn get_i32_array(&self, name: &str) -> Option<Vec<i32>> {
119        let bytes = self.get_raw(name)?;
120        decode_le_array::<{ size_of::<i32>() }, i32>(bytes, i32::from_le_bytes)
121    }
122
123    /// Stores an `f64` metadata entry.
124    pub fn set_f64(&mut self, name: impl Into<String>, value: f64) -> Result<(), DracoError> {
125        self.set_raw(name, value.to_le_bytes().to_vec())
126    }
127
128    /// Reads an `f64` metadata entry.
129    pub fn get_f64(&self, name: &str) -> Option<f64> {
130        let bytes = self.get_raw(name)?;
131        let bytes: [u8; size_of::<f64>()] = bytes.try_into().ok()?;
132        Some(f64::from_le_bytes(bytes))
133    }
134
135    /// Stores an array of `f64` metadata values.
136    pub fn set_f64_array(
137        &mut self,
138        name: impl Into<String>,
139        values: &[f64],
140    ) -> Result<(), DracoError> {
141        let mut bytes = Vec::new();
142        let byte_len = values
143            .len()
144            .checked_mul(size_of::<f64>())
145            .ok_or_else(|| DracoError::InvalidParameter("Metadata array too large".to_string()))?;
146        bytes
147            .try_reserve_exact(byte_len)
148            .map_err(|_| DracoError::InvalidParameter("Metadata array too large".to_string()))?;
149        for value in values {
150            bytes.extend_from_slice(&value.to_le_bytes());
151        }
152        self.set_raw(name, bytes)
153    }
154
155    /// Reads an array of `f64` metadata values.
156    pub fn get_f64_array(&self, name: &str) -> Option<Vec<f64>> {
157        let bytes = self.get_raw(name)?;
158        decode_le_array::<{ size_of::<f64>() }, f64>(bytes, f64::from_le_bytes)
159    }
160
161    /// Stores a UTF-8 string metadata entry.
162    pub fn set_string(
163        &mut self,
164        name: impl Into<String>,
165        value: impl AsRef<str>,
166    ) -> Result<(), DracoError> {
167        self.set_raw(name, value.as_ref().as_bytes().to_vec())
168    }
169
170    /// Reads a UTF-8 string metadata entry.
171    pub fn get_string(&self, name: &str) -> Option<&str> {
172        str::from_utf8(self.get_raw(name)?).ok()
173    }
174
175    /// Removes and returns a raw metadata entry.
176    pub fn remove_entry(&mut self, name: &str) -> Option<Vec<u8>> {
177        self.entries.remove(name)
178    }
179
180    /// Returns a nested metadata block by name.
181    pub fn get_sub_metadata(&self, name: &str) -> Option<&Metadata> {
182        self.sub_metadata.get(name)
183    }
184
185    /// Returns a mutable nested metadata block by name.
186    pub fn get_sub_metadata_mut(&mut self, name: &str) -> Option<&mut Metadata> {
187        self.sub_metadata.get_mut(name)
188    }
189
190    /// Inserts a nested metadata block.
191    pub fn insert_sub_metadata(
192        &mut self,
193        name: impl Into<String>,
194        metadata: Metadata,
195    ) -> Result<(), DracoError> {
196        let name = name.into();
197        validate_metadata_name(&name)?;
198        if self.sub_metadata.contains_key(&name) {
199            return Err(DracoError::DracoError(format!(
200                "Duplicate sub-metadata name: {name}"
201            )));
202        }
203        self.sub_metadata.insert(name, metadata);
204        Ok(())
205    }
206
207    #[cfg(feature = "decoder")]
208    /// Decodes a metadata block from a Draco bitstream buffer.
209    pub fn decode(buffer: &mut DecoderBuffer<'_>) -> Result<Self, DracoError> {
210        Self::decode_with_depth(buffer, 0)
211    }
212
213    #[cfg(feature = "decoder")]
214    fn decode_with_depth(buffer: &mut DecoderBuffer<'_>, depth: usize) -> Result<Self, DracoError> {
215        if depth > MAX_METADATA_NESTING_DEPTH {
216            return Err(DracoError::DracoError(
217                "Metadata nesting depth exceeded".to_string(),
218            ));
219        }
220
221        let num_entries = decode_bounded_count(buffer, "metadata entries count")?;
222        let mut metadata = Metadata::new();
223        for _ in 0..num_entries {
224            let name = decode_name(buffer)?;
225            let data_size = usize::try_from(buffer.decode_varint()?).map_err(|_| {
226                DracoError::DracoError("Metadata entry data size too large".to_string())
227            })?;
228            if data_size == 0 {
229                return Err(DracoError::DracoError(
230                    "Invalid metadata entry data size".to_string(),
231                ));
232            }
233            if data_size > buffer.remaining_size() {
234                return Err(DracoError::DracoError(
235                    "Failed to read metadata entry value".to_string(),
236                ));
237            }
238            let value = buffer.decode_slice(data_size)?.to_vec();
239            metadata.entries.insert(name, value);
240        }
241
242        let num_sub_metadata = decode_bounded_count(buffer, "sub-metadata count")?;
243        if num_sub_metadata > buffer.remaining_size() {
244            return Err(DracoError::DracoError(
245                "Invalid sub-metadata count".to_string(),
246            ));
247        }
248        for _ in 0..num_sub_metadata {
249            let name = decode_name(buffer)?;
250            if metadata.sub_metadata.contains_key(&name) {
251                return Err(DracoError::DracoError(format!(
252                    "Duplicate sub-metadata name: {name}"
253                )));
254            }
255            let sub_metadata = Self::decode_with_depth(buffer, depth + 1)?;
256            metadata.sub_metadata.insert(name, sub_metadata);
257        }
258
259        Ok(metadata)
260    }
261
262    #[cfg(feature = "encoder")]
263    /// Encodes this metadata block to a Draco bitstream buffer.
264    pub fn encode(&self, buffer: &mut EncoderBuffer) -> Result<(), DracoError> {
265        buffer.encode_varint(self.entries.len() as u64);
266        for (name, value) in &self.entries {
267            encode_name(buffer, name)?;
268            if value.is_empty() {
269                return Err(DracoError::InvalidParameter(
270                    "Metadata entry value must not be empty".to_string(),
271                ));
272            }
273            buffer.encode_varint(value.len() as u64);
274            buffer.encode_data(value);
275        }
276
277        buffer.encode_varint(self.sub_metadata.len() as u64);
278        for (name, metadata) in &self.sub_metadata {
279            encode_name(buffer, name)?;
280            metadata.encode(buffer)?;
281        }
282
283        Ok(())
284    }
285}
286
287impl AttributeMetadata {
288    /// Creates metadata for one attribute unique id.
289    pub fn new(attribute_unique_id: u32, metadata: Metadata) -> Self {
290        Self {
291            attribute_unique_id,
292            metadata,
293        }
294    }
295
296    /// Returns the Draco attribute unique id.
297    pub fn attribute_unique_id(&self) -> u32 {
298        self.attribute_unique_id
299    }
300
301    /// Returns the metadata payload.
302    pub fn metadata(&self) -> &Metadata {
303        &self.metadata
304    }
305
306    /// Returns the mutable metadata payload.
307    pub fn metadata_mut(&mut self) -> &mut Metadata {
308        &mut self.metadata
309    }
310}
311
312impl GeometryMetadata {
313    /// Creates empty geometry metadata.
314    pub fn new() -> Self {
315        Self::default()
316    }
317
318    /// Returns true when no geometry or attribute metadata is present.
319    pub fn is_empty(&self) -> bool {
320        self.metadata.is_empty() && self.attribute_metadata.is_empty()
321    }
322
323    /// Returns geometry-level metadata.
324    pub fn metadata(&self) -> &Metadata {
325        &self.metadata
326    }
327
328    /// Returns mutable geometry-level metadata.
329    pub fn metadata_mut(&mut self) -> &mut Metadata {
330        &mut self.metadata
331    }
332
333    /// Returns all per-attribute metadata blocks.
334    pub fn attribute_metadata(&self) -> &[AttributeMetadata] {
335        &self.attribute_metadata
336    }
337
338    /// Finds per-attribute metadata by Draco attribute unique id.
339    pub fn attribute_metadata_by_unique_id(
340        &self,
341        attribute_unique_id: u32,
342    ) -> Option<&AttributeMetadata> {
343        self.attribute_metadata
344            .iter()
345            .find(|metadata| metadata.attribute_unique_id == attribute_unique_id)
346    }
347
348    /// Finds per-attribute metadata by a string metadata entry.
349    pub fn attribute_metadata_by_string_entry(
350        &self,
351        entry_name: &str,
352        entry_value: &str,
353    ) -> Option<&AttributeMetadata> {
354        self.attribute_metadata
355            .iter()
356            .find(|metadata| metadata.metadata.get_string(entry_name) == Some(entry_value))
357    }
358
359    /// Finds mutable per-attribute metadata by Draco attribute unique id.
360    pub fn attribute_metadata_by_unique_id_mut(
361        &mut self,
362        attribute_unique_id: u32,
363    ) -> Option<&mut AttributeMetadata> {
364        self.attribute_metadata
365            .iter_mut()
366            .find(|metadata| metadata.attribute_unique_id == attribute_unique_id)
367    }
368
369    /// Inserts or replaces metadata for one Draco attribute unique id.
370    pub fn set_attribute_metadata(&mut self, attribute_unique_id: u32, metadata: Metadata) {
371        if let Some(existing) = self.attribute_metadata_by_unique_id_mut(attribute_unique_id) {
372            existing.metadata = metadata;
373        } else {
374            self.attribute_metadata
375                .push(AttributeMetadata::new(attribute_unique_id, metadata));
376        }
377    }
378
379    #[cfg(feature = "decoder")]
380    /// Decodes geometry metadata from a Draco bitstream buffer.
381    pub fn decode(buffer: &mut DecoderBuffer<'_>) -> Result<Self, DracoError> {
382        let num_attribute_metadata = decode_bounded_count(buffer, "attribute metadata count")?;
383        let mut geometry_metadata = GeometryMetadata::new();
384        geometry_metadata
385            .attribute_metadata
386            .try_reserve_exact(num_attribute_metadata)
387            .map_err(|_| DracoError::DracoError("Failed to allocate metadata".to_string()))?;
388
389        for _ in 0..num_attribute_metadata {
390            let attribute_unique_id = u32::try_from(buffer.decode_varint()?).map_err(|_| {
391                DracoError::DracoError("Attribute metadata unique id too large".to_string())
392            })?;
393            let metadata = Metadata::decode(buffer)?;
394            geometry_metadata
395                .attribute_metadata
396                .push(AttributeMetadata::new(attribute_unique_id, metadata));
397        }
398        geometry_metadata.metadata = Metadata::decode(buffer)?;
399        Ok(geometry_metadata)
400    }
401
402    #[cfg(feature = "encoder")]
403    /// Encodes geometry metadata to a Draco bitstream buffer.
404    pub fn encode(&self, buffer: &mut EncoderBuffer) -> Result<(), DracoError> {
405        buffer.encode_varint(self.attribute_metadata.len() as u64);
406        for attribute_metadata in &self.attribute_metadata {
407            buffer.encode_varint(attribute_metadata.attribute_unique_id as u64);
408            attribute_metadata.metadata.encode(buffer)?;
409        }
410        self.metadata.encode(buffer)
411    }
412}
413
414fn decode_le_array<const N: usize, T>(
415    bytes: &[u8],
416    decode: impl Fn([u8; N]) -> T,
417) -> Option<Vec<T>> {
418    if bytes.is_empty() || !bytes.len().is_multiple_of(N) {
419        return None;
420    }
421
422    let mut values = Vec::with_capacity(bytes.len() / N);
423    for chunk in bytes.chunks_exact(N) {
424        let mut item = [0u8; N];
425        item.copy_from_slice(chunk);
426        values.push(decode(item));
427    }
428    Some(values)
429}
430
431fn validate_metadata_name(name: &str) -> Result<(), DracoError> {
432    if name.len() > MAX_METADATA_NAME_LEN {
433        return Err(DracoError::InvalidParameter(format!(
434            "Metadata name too long: {} bytes",
435            name.len()
436        )));
437    }
438    Ok(())
439}
440
441#[cfg(feature = "encoder")]
442fn encode_name(buffer: &mut EncoderBuffer, name: &str) -> Result<(), DracoError> {
443    validate_metadata_name(name)?;
444    buffer.encode_u8(name.len() as u8);
445    if !name.is_empty() {
446        buffer.encode_data(name.as_bytes());
447    }
448    Ok(())
449}
450
451#[cfg(feature = "decoder")]
452fn decode_name(buffer: &mut DecoderBuffer<'_>) -> Result<String, DracoError> {
453    let name_len = buffer.decode_u8()? as usize;
454    if name_len > buffer.remaining_size() {
455        return Err(DracoError::DracoError(
456            "Failed to read metadata name".to_string(),
457        ));
458    }
459    let name = buffer.decode_slice(name_len)?.to_vec();
460    String::from_utf8(name)
461        .map_err(|_| DracoError::DracoError("Invalid UTF-8 metadata name".to_string()))
462}
463
464#[cfg(feature = "decoder")]
465fn decode_bounded_count(
466    buffer: &mut DecoderBuffer<'_>,
467    label: &'static str,
468) -> Result<usize, DracoError> {
469    let count = usize::try_from(buffer.decode_varint()?)
470        .map_err(|_| DracoError::DracoError(format!("{label} too large")))?;
471    if count > buffer.remaining_size() {
472        return Err(DracoError::DracoError(format!("Invalid {label}")));
473    }
474    Ok(count)
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480
481    fn encode_metadata(metadata: &Metadata) -> Vec<u8> {
482        let mut buffer = EncoderBuffer::new();
483        metadata.encode(&mut buffer).expect("metadata encode");
484        buffer.data().to_vec()
485    }
486
487    #[test]
488    fn metadata_codec_accepts_empty_metadata() {
489        let metadata = Metadata::new();
490        let bytes = encode_metadata(&metadata);
491        assert_eq!(bytes, vec![0, 0]);
492
493        let mut buffer = DecoderBuffer::new(&bytes);
494        assert_eq!(Metadata::decode(&mut buffer).unwrap(), metadata);
495    }
496
497    #[test]
498    fn metadata_codec_roundtrips_raw_entry() {
499        let mut metadata = Metadata::new();
500        metadata.set_raw("name", b"value".to_vec()).unwrap();
501
502        let bytes = encode_metadata(&metadata);
503        let mut buffer = DecoderBuffer::new(&bytes);
504        let decoded = Metadata::decode(&mut buffer).unwrap();
505
506        assert_eq!(decoded.get_raw("name"), Some(b"value".as_slice()));
507    }
508
509    #[test]
510    fn metadata_typed_helpers_encode_cpp_compatible_bytes() {
511        let mut metadata = Metadata::new();
512        metadata.set_i32("i32", -123).unwrap();
513        metadata.set_i32_array("i32_array", &[1, -2, 3]).unwrap();
514        metadata.set_f64("f64", 1.25).unwrap();
515        metadata.set_f64_array("f64_array", &[0.5, -2.0]).unwrap();
516        metadata.set_string("string", "metadata").unwrap();
517
518        assert_eq!(
519            metadata.get_raw("i32"),
520            Some((-123i32).to_le_bytes().as_slice())
521        );
522        assert_eq!(
523            metadata.get_raw("i32_array"),
524            Some(
525                [
526                    1i32.to_le_bytes(),
527                    (-2i32).to_le_bytes(),
528                    3i32.to_le_bytes()
529                ]
530                .concat()
531                .as_slice()
532            )
533        );
534        assert_eq!(
535            metadata.get_raw("f64"),
536            Some(1.25f64.to_le_bytes().as_slice())
537        );
538        assert_eq!(
539            metadata.get_raw("f64_array"),
540            Some(
541                [0.5f64.to_le_bytes(), (-2.0f64).to_le_bytes()]
542                    .concat()
543                    .as_slice()
544            )
545        );
546        assert_eq!(metadata.get_raw("string"), Some(b"metadata".as_slice()));
547
548        assert_eq!(metadata.get_i32("i32"), Some(-123));
549        assert_eq!(metadata.get_i32_array("i32_array"), Some(vec![1, -2, 3]));
550        assert_eq!(metadata.get_f64("f64"), Some(1.25));
551        assert_eq!(metadata.get_f64_array("f64_array"), Some(vec![0.5, -2.0]));
552        assert_eq!(metadata.get_string("string"), Some("metadata"));
553    }
554
555    #[test]
556    fn metadata_typed_getters_decode_raw_bytes() {
557        let mut metadata = Metadata::new();
558        metadata
559            .set_raw("i32", 42i32.to_le_bytes().to_vec())
560            .unwrap();
561        metadata
562            .set_raw(
563                "i32_array",
564                [7i32.to_le_bytes(), (-8i32).to_le_bytes()].concat(),
565            )
566            .unwrap();
567        metadata
568            .set_raw("f64", 3.5f64.to_le_bytes().to_vec())
569            .unwrap();
570        metadata
571            .set_raw(
572                "f64_array",
573                [1.0f64.to_le_bytes(), 2.25f64.to_le_bytes()].concat(),
574            )
575            .unwrap();
576        metadata.set_raw("string", b"from raw".to_vec()).unwrap();
577
578        assert_eq!(metadata.get_i32("i32"), Some(42));
579        assert_eq!(metadata.get_i32_array("i32_array"), Some(vec![7, -8]));
580        assert_eq!(metadata.get_f64("f64"), Some(3.5));
581        assert_eq!(metadata.get_f64_array("f64_array"), Some(vec![1.0, 2.25]));
582        assert_eq!(metadata.get_string("string"), Some("from raw"));
583    }
584
585    #[test]
586    fn metadata_typed_getters_reject_wrong_raw_shapes() {
587        let mut metadata = Metadata::new();
588        metadata.set_raw("i32", vec![1, 2, 3]).unwrap();
589        metadata.set_raw("i32_array", vec![1, 2, 3]).unwrap();
590        metadata.set_raw("f64", vec![1, 2, 3, 4]).unwrap();
591        metadata.set_raw("f64_array", vec![1, 2, 3, 4]).unwrap();
592        metadata.set_raw("string", vec![0xff]).unwrap();
593
594        assert_eq!(metadata.get_i32("i32"), None);
595        assert_eq!(metadata.get_i32_array("i32_array"), None);
596        assert_eq!(metadata.get_f64("f64"), None);
597        assert_eq!(metadata.get_f64_array("f64_array"), None);
598        assert_eq!(metadata.get_string("string"), None);
599    }
600
601    #[test]
602    fn metadata_typed_setters_reject_empty_values() {
603        let mut metadata = Metadata::new();
604
605        assert!(metadata.set_string("string", "").is_err());
606        assert!(metadata.set_i32_array("i32_array", &[]).is_err());
607        assert!(metadata.set_f64_array("f64_array", &[]).is_err());
608    }
609
610    #[test]
611    fn metadata_typed_setters_overwrite_existing_entry() {
612        let mut metadata = Metadata::new();
613        metadata.set_i32("key", 5).unwrap();
614        metadata.set_string("key", "value").unwrap();
615
616        assert_eq!(metadata.get_i32("key"), None);
617        assert_eq!(metadata.get_string("key"), Some("value"));
618        assert_eq!(metadata.get_raw("key"), Some(b"value".as_slice()));
619    }
620
621    #[test]
622    fn metadata_codec_encodes_entries_in_sorted_order() {
623        let mut metadata = Metadata::new();
624        metadata.set_raw("z", b"last".to_vec()).unwrap();
625        metadata.set_raw("a", b"first".to_vec()).unwrap();
626
627        let bytes = encode_metadata(&metadata);
628
629        assert_eq!(bytes[0], 2);
630        assert_eq!(bytes[1], 1);
631        assert_eq!(bytes[2], b'a');
632    }
633
634    #[test]
635    fn metadata_codec_roundtrips_nested_metadata() {
636        let mut child = Metadata::new();
637        child.set_raw("child", b"value".to_vec()).unwrap();
638
639        let mut metadata = Metadata::new();
640        metadata.insert_sub_metadata("node", child).unwrap();
641
642        let bytes = encode_metadata(&metadata);
643        let mut buffer = DecoderBuffer::new(&bytes);
644        let decoded = Metadata::decode(&mut buffer).unwrap();
645
646        assert_eq!(
647            decoded
648                .get_sub_metadata("node")
649                .and_then(|metadata| metadata.get_raw("child")),
650            Some(b"value".as_slice())
651        );
652    }
653
654    #[test]
655    fn geometry_metadata_roundtrips_attribute_metadata() {
656        let mut attribute = Metadata::new();
657        attribute.set_raw("semantic", b"POSITION".to_vec()).unwrap();
658
659        let mut geometry = GeometryMetadata::new();
660        geometry.set_attribute_metadata(7, attribute);
661        geometry
662            .metadata_mut()
663            .set_raw("asset", b"fixture".to_vec())
664            .unwrap();
665
666        let mut bytes = EncoderBuffer::new();
667        geometry.encode(&mut bytes).unwrap();
668
669        let mut buffer = DecoderBuffer::new(bytes.data());
670        let decoded = GeometryMetadata::decode(&mut buffer).unwrap();
671
672        assert_eq!(
673            decoded.metadata().get_raw("asset"),
674            Some(b"fixture".as_slice())
675        );
676        assert_eq!(
677            decoded
678                .attribute_metadata_by_unique_id(7)
679                .and_then(|metadata| metadata.metadata().get_raw("semantic")),
680            Some(b"POSITION".as_slice())
681        );
682    }
683
684    #[test]
685    fn geometry_metadata_finds_attribute_metadata_by_string_entry() {
686        let mut invalid_string = Metadata::new();
687        invalid_string.set_raw("name", vec![0xff]).unwrap();
688
689        let mut position = Metadata::new();
690        position.set_string("name", "position").unwrap();
691
692        let mut geometry = GeometryMetadata::new();
693        geometry.set_attribute_metadata(3, invalid_string);
694        geometry.set_attribute_metadata(7, position);
695
696        assert_eq!(
697            geometry
698                .attribute_metadata_by_string_entry("name", "position")
699                .map(AttributeMetadata::attribute_unique_id),
700            Some(7)
701        );
702        assert!(geometry
703            .attribute_metadata_by_string_entry("name", "normal")
704            .is_none());
705    }
706
707    #[test]
708    fn metadata_set_raw_overwrites_existing_entry() {
709        let mut metadata = Metadata::new();
710        metadata.set_raw("key", b"old".to_vec()).unwrap();
711        metadata.set_raw("key", b"new".to_vec()).unwrap();
712
713        assert_eq!(metadata.get_raw("key"), Some(b"new".as_slice()));
714    }
715
716    #[test]
717    fn metadata_decode_rejects_duplicate_sub_metadata() {
718        let bytes = [
719            0, 2, // root: entries=0, sub_metadata=2
720            1, b'a', 0, 0, // sub a: entries=0, sub=0
721            1, b'a', 0, 0, // duplicate sub a
722        ];
723        let mut buffer = DecoderBuffer::new(&bytes);
724
725        assert!(Metadata::decode(&mut buffer).is_err());
726    }
727
728    #[test]
729    fn metadata_decode_rejects_zero_length_entry_value() {
730        let bytes = [1, 1, b'a', 0, 0];
731        let mut buffer = DecoderBuffer::new(&bytes);
732
733        assert!(Metadata::decode(&mut buffer).is_err());
734    }
735
736    #[test]
737    fn metadata_decode_rejects_truncated_name() {
738        let bytes = [1, 5, b'a'];
739        let mut buffer = DecoderBuffer::new(&bytes);
740
741        assert!(Metadata::decode(&mut buffer).is_err());
742    }
743
744    #[test]
745    fn metadata_decode_rejects_truncated_value() {
746        let bytes = [1, 1, b'a', 4, 1, 2];
747        let mut buffer = DecoderBuffer::new(&bytes);
748
749        assert!(Metadata::decode(&mut buffer).is_err());
750    }
751
752    #[test]
753    fn metadata_decode_rejects_unreasonable_sub_metadata_count() {
754        let bytes = [0, 5];
755        let mut buffer = DecoderBuffer::new(&bytes);
756
757        assert!(Metadata::decode(&mut buffer).is_err());
758    }
759
760    #[test]
761    fn metadata_decode_rejects_excessive_nesting() {
762        let mut bytes = Vec::new();
763        for _ in 0..=MAX_METADATA_NESTING_DEPTH {
764            bytes.extend_from_slice(&[0, 1, 1, b'a']);
765        }
766        bytes.extend_from_slice(&[0, 0]);
767
768        let mut buffer = DecoderBuffer::new(&bytes);
769        assert!(Metadata::decode(&mut buffer).is_err());
770    }
771}