1 | use serde::Serialize; |
2 | use std::io::{Seek, Write}; |
3 | |
4 | #[cfg (unix)] |
5 | use std::os::fd::OwnedFd; |
6 | |
7 | #[cfg (feature = "gvariant" )] |
8 | use crate::gvariant::Serializer as GVSerializer; |
9 | use crate::{ |
10 | container_depths::ContainerDepths, |
11 | dbus::Serializer as DBusSerializer, |
12 | serialized::{Context, Data, Format, Size, Written}, |
13 | signature_parser::SignatureParser, |
14 | utils::*, |
15 | Basic, DynamicType, Error, Result, Signature, WriteBytes, |
16 | }; |
17 | |
18 | struct NullWriteSeek; |
19 | |
20 | impl Write for NullWriteSeek { |
21 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
22 | Ok(buf.len()) |
23 | } |
24 | |
25 | fn flush(&mut self) -> std::io::Result<()> { |
26 | Ok(()) |
27 | } |
28 | } |
29 | |
30 | impl Seek for NullWriteSeek { |
31 | fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> { |
32 | Ok(u64::MAX) // should never read the return value! |
33 | } |
34 | } |
35 | |
36 | /// Calculate the serialized size of `T`. |
37 | /// |
38 | /// # Examples |
39 | /// |
40 | /// ``` |
41 | /// use zvariant::{serialized::Context, serialized_size, LE}; |
42 | /// |
43 | /// let ctxt = Context::new_dbus(LE, 0); |
44 | /// let len = serialized_size(ctxt, "hello world" ).unwrap(); |
45 | /// assert_eq!(*len, 16); |
46 | /// |
47 | /// let len = serialized_size(ctxt, &("hello world!" , 42_u64)).unwrap(); |
48 | /// assert_eq!(*len, 32); |
49 | /// ``` |
50 | pub fn serialized_size<T>(ctxt: Context, value: &T) -> Result<Size> |
51 | where |
52 | T: ?Sized + Serialize + DynamicType, |
53 | { |
54 | let mut null = NullWriteSeek; |
55 | let signature = value.dynamic_signature(); |
56 | #[cfg (unix)] |
57 | let mut fds = FdList::Number(0); |
58 | |
59 | let len = match ctxt.format() { |
60 | Format::DBus => { |
61 | let mut ser = DBusSerializer::<NullWriteSeek>::new( |
62 | signature, |
63 | &mut null, |
64 | #[cfg (unix)] |
65 | &mut fds, |
66 | ctxt, |
67 | )?; |
68 | value.serialize(&mut ser)?; |
69 | ser.0.bytes_written |
70 | } |
71 | #[cfg (feature = "gvariant" )] |
72 | Format::GVariant => { |
73 | let mut ser = GVSerializer::<NullWriteSeek>::new( |
74 | signature, |
75 | &mut null, |
76 | #[cfg (unix)] |
77 | &mut fds, |
78 | ctxt, |
79 | )?; |
80 | value.serialize(&mut ser)?; |
81 | ser.0.bytes_written |
82 | } |
83 | }; |
84 | |
85 | let size = Size::new(len, ctxt); |
86 | #[cfg (unix)] |
87 | let size = match fds { |
88 | FdList::Number(n) => size.set_num_fds(n), |
89 | FdList::Fds(_) => unreachable!("`Fds::Fds` is not possible here" ), |
90 | }; |
91 | |
92 | Ok(size) |
93 | } |
94 | |
95 | /// Serialize `T` to the given `writer`. |
96 | /// |
97 | /// # Examples |
98 | /// |
99 | /// ``` |
100 | /// use zvariant::{serialized::{Context, Data}, to_writer, LE}; |
101 | /// |
102 | /// let ctxt = Context::new_dbus(LE, 0); |
103 | /// let mut cursor = std::io::Cursor::new(vec![]); |
104 | /// // SAFETY: No FDs are being serialized here so its completely safe. |
105 | /// unsafe { to_writer(&mut cursor, ctxt, &42u32) }.unwrap(); |
106 | /// let encoded = Data::new(cursor.get_ref(), ctxt); |
107 | /// let value: u32 = encoded.deserialize().unwrap().0; |
108 | /// assert_eq!(value, 42); |
109 | /// ``` |
110 | /// |
111 | /// # Safety |
112 | /// |
113 | /// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore |
114 | /// the caller is responsible for not dropping the returned [`Written`] instance before the |
115 | /// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while |
116 | /// serialized data will still refer to them. Hence why this function is marked unsafe. |
117 | /// |
118 | /// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and |
119 | /// hence is safe to drop. |
120 | /// |
121 | /// [`to_writer_fds`]: fn.to_writer_fds.html |
122 | pub unsafe fn to_writer<W, T>(writer: &mut W, ctxt: Context, value: &T) -> Result<Written> |
123 | where |
124 | W: Write + Seek, |
125 | T: ?Sized + Serialize + DynamicType, |
126 | { |
127 | let signature: Signature<'_> = value.dynamic_signature(); |
128 | |
129 | to_writer_for_signature(writer, ctxt, &signature, value) |
130 | } |
131 | |
132 | /// Serialize `T` as a byte vector. |
133 | /// |
134 | /// See [`Data::deserialize`] documentation for an example of how to use this function. |
135 | pub fn to_bytes<T>(ctxt: Context, value: &T) -> Result<Data<'static, 'static>> |
136 | where |
137 | T: ?Sized + Serialize + DynamicType, |
138 | { |
139 | to_bytes_for_signature(ctxt, value.dynamic_signature(), value) |
140 | } |
141 | |
142 | /// Serialize `T` that has the given signature, to the given `writer`. |
143 | /// |
144 | /// Use this function instead of [`to_writer`] if the value being serialized does not implement |
145 | /// [`DynamicType`]. |
146 | /// |
147 | /// # Safety |
148 | /// |
149 | /// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore |
150 | /// the caller is responsible for not dropping the returned [`Written`] instance before the |
151 | /// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while |
152 | /// serialized data will still refer to them. Hence why this function is marked unsafe. |
153 | /// |
154 | /// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and |
155 | /// hence is safe to drop. |
156 | /// |
157 | /// [`to_writer`]: fn.to_writer.html |
158 | pub unsafe fn to_writer_for_signature<'s, W, S, T>( |
159 | writer: &mut W, |
160 | ctxt: Context, |
161 | signature: S, |
162 | value: &T, |
163 | ) -> Result<Written> |
164 | where |
165 | W: Write + Seek, |
166 | S: TryInto<Signature<'s>>, |
167 | S::Error: Into<Error>, |
168 | T: ?Sized + Serialize, |
169 | { |
170 | #[cfg (unix)] |
171 | let mut fds = FdList::Fds(vec![]); |
172 | |
173 | let len = match ctxt.format() { |
174 | Format::DBus => { |
175 | let mut ser = DBusSerializer::<W>::new( |
176 | signature, |
177 | writer, |
178 | #[cfg (unix)] |
179 | &mut fds, |
180 | ctxt, |
181 | )?; |
182 | value.serialize(&mut ser)?; |
183 | ser.0.bytes_written |
184 | } |
185 | #[cfg (feature = "gvariant" )] |
186 | Format::GVariant => { |
187 | let mut ser = GVSerializer::<W>::new( |
188 | signature, |
189 | writer, |
190 | #[cfg (unix)] |
191 | &mut fds, |
192 | ctxt, |
193 | )?; |
194 | value.serialize(&mut ser)?; |
195 | ser.0.bytes_written |
196 | } |
197 | }; |
198 | |
199 | let written = Written::new(len, ctxt); |
200 | #[cfg (unix)] |
201 | let written = match fds { |
202 | FdList::Fds(fds) => written.set_fds(fds), |
203 | FdList::Number(_) => unreachable!("`Fds::Number` is not possible here" ), |
204 | }; |
205 | |
206 | Ok(written) |
207 | } |
208 | |
209 | /// Serialize `T` that has the given signature, to a new byte vector. |
210 | /// |
211 | /// Use this function instead of [`to_bytes`] if the value being serialized does not implement |
212 | /// [`DynamicType`]. See [`from_slice_for_signature`] documentation for an example of how to use |
213 | /// this function. |
214 | /// |
215 | /// [`to_bytes`]: fn.to_bytes.html |
216 | /// [`from_slice_for_signature`]: fn.from_slice_for_signature.html#examples |
217 | pub fn to_bytes_for_signature<'s, S, T>( |
218 | ctxt: Context, |
219 | signature: S, |
220 | value: &T, |
221 | ) -> Result<Data<'static, 'static>> |
222 | where |
223 | S: TryInto<Signature<'s>>, |
224 | S::Error: Into<Error>, |
225 | T: ?Sized + Serialize, |
226 | { |
227 | let mut cursor: Cursor> = std::io::Cursor::new(inner:vec![]); |
228 | // SAFETY: We put the bytes and FDs in the `Data` to ensure that the data and FDs are only |
229 | // dropped together. |
230 | let ret: Written = unsafe { to_writer_for_signature(&mut cursor, ctxt, signature, value) }?; |
231 | #[cfg (unix)] |
232 | let encoded: Data<'_, 'static> = Data::new_fds(bytes:cursor.into_inner(), context:ctxt, ret.into_fds()); |
233 | #[cfg (not(unix))] |
234 | let encoded = { |
235 | let _ = ret; |
236 | Data::new(cursor.into_inner(), ctxt) |
237 | }; |
238 | |
239 | Ok(encoded) |
240 | } |
241 | |
242 | /// Context for all our serializers and provides shared functionality. |
243 | pub(crate) struct SerializerCommon<'ser, 'sig, W> { |
244 | pub(crate) ctxt: Context, |
245 | pub(crate) writer: &'ser mut W, |
246 | pub(crate) bytes_written: usize, |
247 | #[cfg (unix)] |
248 | pub(crate) fds: &'ser mut FdList, |
249 | |
250 | pub(crate) sig_parser: SignatureParser<'sig>, |
251 | |
252 | pub(crate) value_sign: Option<Signature<'static>>, |
253 | |
254 | pub(crate) container_depths: ContainerDepths, |
255 | } |
256 | |
257 | #[cfg (unix)] |
258 | pub(crate) enum FdList { |
259 | Fds(Vec<OwnedFd>), |
260 | Number(u32), |
261 | } |
262 | |
263 | impl<'ser, 'sig, W> SerializerCommon<'ser, 'sig, W> |
264 | where |
265 | W: Write + Seek, |
266 | { |
267 | #[cfg (unix)] |
268 | pub(crate) fn add_fd(&mut self, fd: std::os::fd::RawFd) -> Result<u32> { |
269 | use std::os::fd::{AsRawFd, BorrowedFd}; |
270 | |
271 | match self.fds { |
272 | FdList::Fds(fds) => { |
273 | if let Some(idx) = fds.iter().position(|x| x.as_raw_fd() == fd) { |
274 | return Ok(idx as u32); |
275 | } |
276 | let idx = fds.len(); |
277 | // Cloning implies dup and is unfortunate but we need to return owned fds |
278 | // and dup is not expensive (at least on Linux). |
279 | let fd = unsafe { BorrowedFd::borrow_raw(fd) }.try_clone_to_owned()?; |
280 | fds.push(fd); |
281 | |
282 | Ok(idx as u32) |
283 | } |
284 | FdList::Number(n) => { |
285 | let idx = *n; |
286 | *n += 1; |
287 | |
288 | Ok(idx) |
289 | } |
290 | } |
291 | } |
292 | |
293 | pub(crate) fn add_padding(&mut self, alignment: usize) -> Result<usize> { |
294 | let padding = padding_for_n_bytes(self.abs_pos(), alignment); |
295 | if padding > 0 { |
296 | let byte = [0_u8; 1]; |
297 | for _ in 0..padding { |
298 | self.write_all(&byte) |
299 | .map_err(|e| Error::InputOutput(e.into()))?; |
300 | } |
301 | } |
302 | |
303 | Ok(padding) |
304 | } |
305 | |
306 | pub(crate) fn prep_serialize_basic<T>(&mut self) -> Result<()> |
307 | where |
308 | T: Basic, |
309 | { |
310 | self.sig_parser.skip_char()?; |
311 | self.add_padding(T::alignment(self.ctxt.format()))?; |
312 | |
313 | Ok(()) |
314 | } |
315 | |
316 | /// This starts the enum serialization. |
317 | /// |
318 | /// It's up to the caller to do the rest: serialize the variant payload and skip the `). |
319 | pub(crate) fn prep_serialize_enum_variant(&mut self, variant_index: u32) -> Result<()> { |
320 | // Encode enum variants as a struct with first field as variant index |
321 | let signature = self.sig_parser.next_signature()?; |
322 | if self.sig_parser.next_char()? != STRUCT_SIG_START_CHAR { |
323 | return Err(Error::SignatureMismatch( |
324 | signature.to_owned(), |
325 | format!("expected ` {STRUCT_SIG_START_CHAR}`" ), |
326 | )); |
327 | } |
328 | |
329 | let alignment = alignment_for_signature(&signature, self.ctxt.format())?; |
330 | self.add_padding(alignment)?; |
331 | |
332 | // Now serialize the veriant index. |
333 | self.write_u32(self.ctxt.endian(), variant_index) |
334 | .map_err(|e| Error::InputOutput(e.into()))?; |
335 | |
336 | // Skip the `(`, `u`. |
337 | self.sig_parser.skip_chars(2)?; |
338 | |
339 | Ok(()) |
340 | } |
341 | |
342 | fn abs_pos(&self) -> usize { |
343 | self.ctxt.position() + self.bytes_written |
344 | } |
345 | } |
346 | |
347 | impl<'ser, 'sig, W> Write for SerializerCommon<'ser, 'sig, W> |
348 | where |
349 | W: Write + Seek, |
350 | { |
351 | /// Write `buf` and increment internal bytes written counter. |
352 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
353 | self.writer.write(buf).map(|n: usize| { |
354 | self.bytes_written += n; |
355 | |
356 | n |
357 | }) |
358 | } |
359 | |
360 | fn flush(&mut self) -> std::io::Result<()> { |
361 | self.writer.flush() |
362 | } |
363 | } |
364 | |