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}