sheave_core/messages/amf/v0/
ecma_array.rs

1use std::io::Result as IOResult;
2use log::warn;
3use crate::{
4    Decoder,
5    Encoder,
6    ByteBuffer,
7    messages::amf::{
8        ensure_marker,
9        v0::Marker
10    }
11};
12use super::Properties;
13
14/// The **sized** object type of AMF.
15///
16/// This consists of:
17///
18/// * Count
19///
20/// The unsigned 32 bits integer.
21///
22/// This consists of pairs of string keys and any AMF data types.
23///
24/// * Key
25///
26/// The string which doesn't have its marker.
27/// This type is named as `UnmarkedString` in this crate.
28/// Also this occurs the panic if its length exceeds the range of 16 bits.
29///
30/// * Value
31///
32/// The pointer for AMF data types, which is wrapped into `Arc`.
33/// This is because of avoiding to be deallocated its value unexpectedly.
34///
35/// This is expected its size is same as the above count.
36///
37/// You can access to properties which this contains, as the `HashMap`.
38///
39/// # Examples
40///
41/// ```rust
42/// use sheave_core::{
43///     ecma_array,
44///     messages::amf::v0::{
45///         EcmaArray,
46///         Number
47///     },
48/// };
49///
50/// let ecma_array = ecma_array!(
51///     "videocodecid" => Number::from(0)
52/// );
53/// ecma_array.get_properties().get("videocodecid");
54/// &ecma_array.get_properties()["videocodecid"];
55/// ```
56#[derive(Debug, Clone, Default, PartialEq, Eq)]
57pub struct EcmaArray(Properties);
58
59impl EcmaArray {
60    /// Constructs a new ECMA array.
61    pub fn new(properties: Properties) -> Self {
62        Self(properties)
63    }
64
65    /// Gets immutable properties from this array.
66    pub fn get_properties(&self) -> &Properties {
67        &self.0
68    }
69
70    /// Gets mutable properties from this array.
71    pub fn get_properties_mut(&mut self) -> &mut Properties {
72        &mut self.0
73    }
74}
75
76impl Decoder<EcmaArray> for ByteBuffer {
77    /// Decodes bytes into an ECMA array.
78    ///
79    /// # Errors
80    ///
81    /// * [`InsufficientBufferLength`]
82    ///
83    /// When buffer isn't remained at least 2 bytes. (non-empty ECMA array contains at least one pair of key and value)
84    ///
85    /// * [`InconsistentMarker`]
86    ///
87    /// When a marker byte doesn't indicate the ECMA array.
88    ///
89    /// * [`InvalidString`]
90    ///
91    /// When key bytes are invalid for a UTF-8 string.
92    ///
93    /// # Examples
94    ///
95    /// ```rust
96    /// use sheave_core::{
97    ///     ByteBuffer,
98    ///     Decoder,
99    ///     messages::amf::v0::{
100    ///         Marker,
101    ///         EcmaArray
102    ///     }
103    /// };
104    ///
105    /// let mut buffer = ByteBuffer::default();
106    /// buffer.put_u8(Marker::EcmaArray as u8);
107    /// buffer.put_u32_be(0);
108    /// // Also ECMA array type is required a marker of object end (0x09) which is associated with an empty key.
109    /// buffer.put_u16_be(0);
110    /// buffer.put_u8(Marker::ObjectEnd as u8);
111    /// assert!(Decoder::<EcmaArray>::decode(&mut buffer).is_ok());
112    ///
113    /// let mut buffer = ByteBuffer::default();
114    /// buffer.put_u8(Marker::Other as u8);
115    /// buffer.put_u32_be(0);
116    /// buffer.put_u16_be(0);
117    /// buffer.put_u8(Marker::ObjectEnd as u8);
118    /// assert!(Decoder::<EcmaArray>::decode(&mut buffer).is_err());
119    ///
120    /// // This is a missing sequence of the "sparkle heart(💖)".
121    /// let mut bytes = vec![0, 159, 146, 150];
122    /// let mut buffer = ByteBuffer::default();
123    /// buffer.put_u8(Marker::EcmaArray as u8);
124    /// buffer.put_u32_be(0);
125    /// buffer.put_u16_be(4);
126    /// buffer.put_bytes(&bytes);
127    /// buffer.put_u8(Marker::Number as u8);
128    /// buffer.put_f64(0.0);
129    /// buffer.put_u16_be(0);
130    /// buffer.put_u8(Marker::ObjectEnd as u8);
131    /// assert!(Decoder::<EcmaArray>::decode(&mut buffer).is_err());
132    ///
133    /// let mut buffer = ByteBuffer::default();
134    /// assert!(Decoder::<EcmaArray>::decode(&mut buffer).is_err())
135    /// ```
136    ///
137    /// Note the length field will not be so cared because to decode is enough to check the object end marker (0x09).
138    /// However warning will emit if lengths is inconsistent.
139    ///
140    /// [`InsufficientBufferLength`]: crate::byte_buffer::InsufficientBufferLength
141    /// [`InconsistentMarker`]: crate::messages::amf::InconsistentMarker
142    /// [`InvalidString`]: crate::messages::amf::InvalidString
143    fn decode(&mut self) -> IOResult<EcmaArray> {
144        self.get_u8().and_then(
145            |marker| ensure_marker(Marker::EcmaArray as u8, marker)
146        )?;
147
148        let length = self.get_u32_be()?;
149        let properties: Properties = self.decode()?;
150
151        if properties.len() != length as usize {
152            warn!("Properties length doesn't match previous field: previous field: {length}, actual length: {}", properties.len());
153        }
154
155        Ok(EcmaArray(properties))
156    }
157}
158
159impl Encoder<EcmaArray> for ByteBuffer {
160    /// Encodes an ECMA array into bytes.
161    ///
162    /// # Panics
163    ///
164    /// Its length must be the range of 32 bits.
165    /// If it exceeds, a panic is occured.
166    fn encode(&mut self, ecma_array: &EcmaArray) {
167        assert!(ecma_array.0.len() <= u32::MAX as usize);
168
169        self.put_u8(Marker::EcmaArray as u8);
170        self.put_u32_be(ecma_array.0.len() as u32);
171        self.encode(&ecma_array.0);
172    }
173}
174
175/// Constructs an ECMA array.
176///
177/// # Examples
178///
179/// ```rust
180/// use sheave_core::{
181///     ecma_array,
182///     messages::amf::v0::{
183///         EcmaArray,
184///         Number
185///     }
186/// };
187///
188/// let mut on_metadata = EcmaArray::default();
189/// on_metadata.get_properties_mut().insert("videocodecid", Number::from(0));
190/// on_metadata.get_properties_mut().insert("audiocodecid", Number::from(0));
191///
192/// assert_eq!(
193///     on_metadata,
194///     ecma_array!(
195///         "videocodecid" => Number::from(0),
196///         "audiocodecid" => Number::from(0)
197///     )
198/// )
199/// ```
200#[macro_export]
201macro_rules! ecma_array {
202    ($($key:expr => $value:expr),*) => {
203        {
204            use $crate::messages::amf::v0::{
205                EcmaArray,
206                Properties
207            };
208            let mut properties = Properties::default();
209            $(properties.insert($key, $value);)*
210            EcmaArray::new(properties)
211        }
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use crate::messages::amf::v0::UnmarkedString;
218    use super::*;
219
220    #[test]
221    fn decode_ecma_array() {
222        let mut buffer = ByteBuffer::default();
223        buffer.put_u8(Marker::EcmaArray as u8);
224        buffer.put_u32_be(0);
225        buffer.encode(&UnmarkedString::from(""));
226        buffer.put_u8(Marker::ObjectEnd as u8);
227        let result: IOResult<EcmaArray> = buffer.decode();
228        assert!(result.is_ok());
229        let actual = result.unwrap();
230        assert_eq!(EcmaArray::default(), actual)
231    }
232
233    #[test]
234    fn encode_ecma_array() {
235        let mut buffer = ByteBuffer::default();
236        buffer.encode(&EcmaArray::default());
237        let result: Vec<u8> = buffer.into();
238        assert_eq!(Marker::EcmaArray as u8, result[0]);
239        assert_eq!(&0u32.to_be_bytes(), &result[1..5]);
240        assert_eq!(&0u16.to_be_bytes(), &result[5..7]);
241        assert_eq!(Marker::ObjectEnd as u8, result[7])
242    }
243}