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