x11rb/connection/
mod.rs

1//! Generic connection-related types and definitions.
2//!
3//! This module contains the `Connection` trait and related definitions. The code in this module is
4//! used by each concrete implementation of the X11 protocol.
5
6use std::io::IoSlice;
7
8use x11rb_protocol::x11_utils::{ReplyFDsRequest, ReplyRequest, VoidRequest};
9
10use crate::cookie::{Cookie, CookieWithFds, VoidCookie};
11use crate::errors::{ConnectionError, ParseError, ReplyError, ReplyOrIdError};
12use crate::protocol::xproto::Setup;
13use crate::protocol::Event;
14use crate::utils::RawFdContainer;
15use crate::x11_utils::{ExtensionInformation, TryParse, TryParseFd, X11Error};
16
17pub use x11rb_protocol::{DiscardMode, RawEventAndSeqNumber, SequenceNumber};
18
19mod impls;
20
21// Used to avoid too-complex types.
22/// A combination of a buffer and a list of file descriptors.
23pub type BufWithFds<B> = (B, Vec<RawFdContainer>);
24/// An event and its sequence number.
25pub type EventAndSeqNumber = (Event, SequenceNumber);
26
27/// Either a raw reply or a raw error response to an X11 request.
28#[derive(Debug)]
29pub enum ReplyOrError<R, E = R>
30where
31    R: std::fmt::Debug,
32    E: AsRef<[u8]> + std::fmt::Debug,
33{
34    /// The reply to an X11 request.
35    Reply(R),
36
37    /// An error caused by an X11 request.
38    Error(E),
39}
40
41/// A connection to an X11 server for sending requests.
42///
43/// This trait only contains functions that are used by other parts of this library. This means
44/// that users of this library will most likely not need these functions, unless they want to
45/// implement their own X11 connection.
46pub trait RequestConnection {
47    /// Type used as buffer to store raw replies or events before
48    /// they are parsed.
49    type Buf: AsRef<[u8]> + std::fmt::Debug + Send + Sync + 'static;
50
51    /// Send a request with a reply to the server.
52    ///
53    /// The `bufs` parameter describes the raw bytes that should be sent. The returned cookie
54    /// allows to get the response.
55    ///
56    /// The `fds` parameter contains a list of file descriptors that should be sent with the
57    /// request. Ownership of these FDs is transferred to the connection. This means that the
58    /// connection will close the FDs after they were sent.
59    ///
60    /// Users of this library will most likely not want to use this function directly. Instead, the
61    /// generated code will take the supplied arguments, construct byte buffers, and call this
62    /// method.
63    ///
64    /// The provided buffers must contain at least a single element and the first buffer must have
65    /// at least four bytes. The length field must be set correctly, unless the request is larger
66    /// than 2^18 bytes, because in this case, the length field would overflow. The connection
67    /// automatically uses the BIG-REQUESTS extension for such large requests.
68    ///
69    /// In any case, the request may not be larger than the server's maximum request length.
70    fn send_request_with_reply<R>(
71        &self,
72        bufs: &[IoSlice<'_>],
73        fds: Vec<RawFdContainer>,
74    ) -> Result<Cookie<'_, Self, R>, ConnectionError>
75    where
76        R: TryParse;
77
78    /// Send a request with a reply to the server.
79    ///
80    /// This function is a wrapper around [`RequestConnection::send_request_with_reply`]. This
81    /// function gets a [`ReplyRequest`] as its argument to specify the request to send.
82    fn send_trait_request_with_reply<R>(
83        &self,
84        request: R,
85    ) -> Result<Cookie<'_, Self, <R as ReplyRequest>::Reply>, ConnectionError>
86    where
87        R: ReplyRequest,
88    {
89        let opcode = match R::EXTENSION_NAME {
90            None => 0,
91            Some(extension) => {
92                self.extension_information(extension)?
93                    .ok_or(ConnectionError::UnsupportedExtension)?
94                    .major_opcode
95            }
96        };
97        let (buf, fds) = request.serialize(opcode);
98        self.send_request_with_reply(&[IoSlice::new(&buf)], fds)
99    }
100
101    /// Send a request with a reply containing file descriptors to the server.
102    ///
103    /// The `bufs` parameter describes the raw bytes that should be sent. The returned cookie
104    /// allows to get the response.
105    ///
106    /// The `fds` parameter contains a list of file descriptors that should be sent with the
107    /// request. Ownership of these FDs is transferred to the connection. This means that the
108    /// connection will close the FDs after they were sent.
109    ///
110    /// Users of this library will most likely not want to use this function directly. Instead, the
111    /// generated code will take the supplied arguments, construct byte buffers, and call this
112    /// method.
113    ///
114    /// The provided buffers must contain at least a single element and the first buffer must have
115    /// at least four bytes. The length field must be set correctly, unless the request is larger
116    /// than 2^18 bytes, because in this case, the length field would overflow. The connection
117    /// automatically uses the BIG-REQUESTS extension for such large requests.
118    ///
119    /// In any case, the request may not be larger than the server's maximum request length.
120    fn send_request_with_reply_with_fds<R>(
121        &self,
122        bufs: &[IoSlice<'_>],
123        fds: Vec<RawFdContainer>,
124    ) -> Result<CookieWithFds<'_, Self, R>, ConnectionError>
125    where
126        R: TryParseFd;
127
128    /// Send a request with a reply containing file descriptors to the server.
129    ///
130    /// This function is a wrapper around [`RequestConnection::send_request_with_reply_with_fds`].
131    /// This function gets a [`ReplyFDsRequest`] as its argument to specify the request to send.
132    fn send_trait_request_with_reply_with_fds<R>(
133        &self,
134        request: R,
135    ) -> Result<CookieWithFds<'_, Self, R::Reply>, ConnectionError>
136    where
137        R: ReplyFDsRequest,
138    {
139        let opcode = match R::EXTENSION_NAME {
140            None => 0,
141            Some(extension) => {
142                self.extension_information(extension)?
143                    .ok_or(ConnectionError::UnsupportedExtension)?
144                    .major_opcode
145            }
146        };
147        let (buf, fds) = request.serialize(opcode);
148        self.send_request_with_reply_with_fds(&[IoSlice::new(&buf)], fds)
149    }
150
151    /// Send a request without a reply to the server.
152    ///
153    /// The `bufs` parameter describes the raw bytes that should be sent. The sequence number of
154    /// the request is returned, but most likely not useful to users.
155    ///
156    /// The `fds` parameter contains a list of file descriptors that should be sent with the
157    /// request. Ownership of these FDs is transferred to the connection. This means that the
158    /// connection will close the FDs after they were sent.
159    ///
160    /// Users of this library will most likely not want to use this function directly. Instead, the
161    /// generated code will take the supplied arguments, construct byte buffers, and call this
162    /// method.
163    ///
164    /// The provided buffers must contain at least a single element and the first buffer must have
165    /// at least four bytes. The length field must be set correctly, unless the request is larger
166    /// than 2^18 bytes, because in this case, the length field would overflow. The connection
167    /// automatically uses the BIG-REQUESTS extension for such large requests.
168    ///
169    /// In any case, the request may not be larger than the server's maximum request length.
170    fn send_request_without_reply(
171        &self,
172        bufs: &[IoSlice<'_>],
173        fds: Vec<RawFdContainer>,
174    ) -> Result<VoidCookie<'_, Self>, ConnectionError>;
175
176    /// Send a request without a reply to the server.
177    ///
178    /// This function is a wrapper around [`RequestConnection::send_request_without_reply`]. This
179    /// function gets a [`VoidRequest`] as its argument to specify the request to send.
180    fn send_trait_request_without_reply<R>(
181        &self,
182        request: R,
183    ) -> Result<VoidCookie<'_, Self>, ConnectionError>
184    where
185        R: VoidRequest,
186    {
187        let opcode = match R::EXTENSION_NAME {
188            None => 0,
189            Some(extension) => {
190                self.extension_information(extension)?
191                    .ok_or(ConnectionError::UnsupportedExtension)?
192                    .major_opcode
193            }
194        };
195        let (buf, fds) = request.serialize(opcode);
196        self.send_request_without_reply(&[IoSlice::new(&buf)], fds)
197    }
198
199    /// A reply to an error should be discarded.
200    ///
201    /// This method is automatically called by the `Drop` implementation on `Cookie` so that any
202    /// replies that are received later can be ignored.
203    ///
204    /// Users of this library will most likely not want to use this function directly.
205    fn discard_reply(&self, sequence: SequenceNumber, kind: RequestKind, mode: DiscardMode);
206
207    /// Prefetches information about an extension.
208    ///
209    /// If the information of a extension is not cached yet, this function sends a
210    /// `QueryExtension` request, but it does not wait for the reply.
211    ///
212    /// You can use `extension_information()` to get the reply of such request.
213    ///
214    /// Using this function can help to reduce round-trip latency, but you can use
215    /// `extension_information()` directly without calling this function first.
216    fn prefetch_extension_information(
217        &self,
218        extension_name: &'static str,
219    ) -> Result<(), ConnectionError>;
220
221    /// Get information about an extension.
222    ///
223    /// To send a request for some extension, information about the extension (major opcode,
224    /// first event code and first error code) is necessary. This function provides this
225    /// information.
226    ///
227    /// The returned object is guaranteed to have a non-zero `present` field. Extensions that are
228    /// not present are instead returned as `None`.
229    fn extension_information(
230        &self,
231        extension_name: &'static str,
232    ) -> Result<Option<ExtensionInformation>, ConnectionError>;
233
234    /// Wait for the reply to a request.
235    ///
236    /// The given sequence number identifies the request for which replies are expected. If the X11
237    /// server answered the request with an error, that error is returned as an `Err`.
238    ///
239    /// Users of this library will most likely not want to use this function directly.
240    fn wait_for_reply_or_error(&self, sequence: SequenceNumber) -> Result<Self::Buf, ReplyError> {
241        match self.wait_for_reply_or_raw_error(sequence)? {
242            ReplyOrError::Reply(reply) => Ok(reply),
243            ReplyOrError::Error(error) => {
244                Err(ReplyError::X11Error(self.parse_error(error.as_ref())?))
245            }
246        }
247    }
248
249    /// Wait for the reply to a request.
250    ///
251    /// The given sequence number identifies the request for which replies are expected. If the X11
252    /// server answered the request with an error, that error is returned as an `Err`.
253    ///
254    /// Users of this library will most likely not want to use this function directly.
255    fn wait_for_reply_or_raw_error(
256        &self,
257        sequence: SequenceNumber,
258    ) -> Result<ReplyOrError<Self::Buf>, ConnectionError>;
259
260    /// Wait for the reply to a request.
261    ///
262    /// The given sequence number identifies the request for which replies are expected. If the X11
263    /// server answered the request with an error, this function returns `None` and the error is
264    /// instead returned by `wait_for_event()` or `poll_for_event()`.
265    ///
266    /// Users of this library will most likely not want to use this function directly.
267    fn wait_for_reply(
268        &self,
269        sequence: SequenceNumber,
270    ) -> Result<Option<Self::Buf>, ConnectionError>;
271
272    /// Wait for the reply to a request that has FDs.
273    ///
274    /// The given sequence number identifies the request for which replies are expected.
275    ///
276    /// Users of this library will most likely not want to use this function directly.
277    fn wait_for_reply_with_fds(
278        &self,
279        sequence: SequenceNumber,
280    ) -> Result<BufWithFds<Self::Buf>, ReplyError> {
281        match self.wait_for_reply_with_fds_raw(sequence)? {
282            ReplyOrError::Reply(reply) => Ok(reply),
283            ReplyOrError::Error(error) => {
284                Err(ReplyError::X11Error(self.parse_error(error.as_ref())?))
285            }
286        }
287    }
288
289    /// Wait for the reply to a request that has FDs.
290    ///
291    /// The given sequence number identifies the request for which replies are expected.
292    ///
293    /// Users of this library will most likely not want to use this function directly.
294    fn wait_for_reply_with_fds_raw(
295        &self,
296        sequence: SequenceNumber,
297    ) -> Result<ReplyOrError<BufWithFds<Self::Buf>, Self::Buf>, ConnectionError>;
298
299    /// Check whether a request that does not have a reply caused an X11 error.
300    ///
301    /// The given sequence number identifies the request for which the check should be performed.
302    ///
303    /// Users of this library will most likely not want to use this function directly.
304    fn check_for_error(&self, sequence: SequenceNumber) -> Result<(), ReplyError> {
305        match self.check_for_raw_error(sequence)? {
306            Some(err) => Err(self.parse_error(err.as_ref())?.into()),
307            None => Ok(()),
308        }
309    }
310
311    /// Check whether a request that does not have a reply caused an X11 error.
312    ///
313    /// The given sequence number identifies the request for which the check should be performed.
314    ///
315    /// Users of this library will most likely not want to use this function directly.
316    fn check_for_raw_error(
317        &self,
318        sequence: SequenceNumber,
319    ) -> Result<Option<Self::Buf>, ConnectionError>;
320
321    /// Prefetches the maximum request length.
322    ///
323    /// If the maximum request length is not cached yet, this function sends a `BigRequests::Enable`
324    /// request, but it does not wait for the reply.
325    ///
326    /// You can use `maximum_request_bytes()` to get the result of this request.
327    ///
328    /// Using this function can help to reduce round-trip latency, but you can use
329    /// `maximum_request_bytes()` directly without calling this function first.
330    ///
331    /// Since this uses the `BigRequests` extension, the information about that extension needs to
332    /// available. Otherwise, this has to wait for the reply when calling
333    /// `extension_information()`.
334    ///
335    /// To prefetch the necessary information, you can do the following:
336    /// ```no_run
337    /// use x11rb::connection::RequestConnection;
338    /// use x11rb::errors::ConnectionError;
339    /// use x11rb::protocol::bigreq;
340    /// # fn do_it(conn: impl RequestConnection) -> Result<(), ConnectionError> {
341    /// // conn is a RequestConnection
342    /// conn.prefetch_extension_information(bigreq::X11_EXTENSION_NAME)?;
343    /// # Ok(())
344    /// # }
345    /// ```
346    fn prefetch_maximum_request_bytes(&self);
347
348    /// The maximum number of bytes that the X11 server accepts in a request.
349    fn maximum_request_bytes(&self) -> usize;
350
351    /// Parse a generic error.
352    fn parse_error(&self, error: &[u8]) -> Result<X11Error, ParseError>;
353
354    /// Parse a generic event.
355    fn parse_event(&self, event: &[u8]) -> Result<Event, ParseError>;
356}
357
358/// A connection to an X11 server.
359pub trait Connection: RequestConnection {
360    /// Wait for a new event from the X11 server.
361    fn wait_for_event(&self) -> Result<Event, ConnectionError> {
362        Ok(self.wait_for_event_with_sequence()?.0)
363    }
364
365    /// Wait for a new raw/unparsed event from the X11 server.
366    fn wait_for_raw_event(&self) -> Result<Self::Buf, ConnectionError> {
367        Ok(self.wait_for_raw_event_with_sequence()?.0)
368    }
369
370    /// Wait for a new event from the X11 server.
371    fn wait_for_event_with_sequence(&self) -> Result<EventAndSeqNumber, ConnectionError> {
372        let (event, seq) = self.wait_for_raw_event_with_sequence()?;
373        let event = self.parse_event(event.as_ref())?;
374        Ok((event, seq))
375    }
376
377    /// Wait for a new raw/unparsed event from the X11 server.
378    fn wait_for_raw_event_with_sequence(
379        &self,
380    ) -> Result<RawEventAndSeqNumber<Self::Buf>, ConnectionError>;
381
382    /// Poll for a new event from the X11 server.
383    fn poll_for_event(&self) -> Result<Option<Event>, ConnectionError> {
384        Ok(self.poll_for_event_with_sequence()?.map(|r| r.0))
385    }
386
387    /// Poll for a new raw/unparsed event from the X11 server.
388    fn poll_for_raw_event(&self) -> Result<Option<Self::Buf>, ConnectionError> {
389        Ok(self.poll_for_raw_event_with_sequence()?.map(|r| r.0))
390    }
391
392    /// Poll for a new event from the X11 server.
393    fn poll_for_event_with_sequence(&self) -> Result<Option<EventAndSeqNumber>, ConnectionError> {
394        Ok(match self.poll_for_raw_event_with_sequence()? {
395            Some((event, seq)) => Some((self.parse_event(event.as_ref())?, seq)),
396            None => None,
397        })
398    }
399
400    /// Poll for a new unparsed/raw event from the X11 server.
401    fn poll_for_raw_event_with_sequence(
402        &self,
403    ) -> Result<Option<RawEventAndSeqNumber<Self::Buf>>, ConnectionError>;
404
405    /// Send all pending requests to the server.
406    ///
407    /// Implementations of this trait may buffer requests for batched sending. When this method is
408    /// called, all pending requests are sent.
409    ///
410    /// You do not have to call this method before `wait_for_reply()`. If the request you want to
411    /// wait for was not yet sent, it will be sent by `wait_for_reply()`.
412    fn flush(&self) -> Result<(), ConnectionError>;
413
414    /// Get the setup information sent by the X11 server.
415    ///
416    /// The setup information contains X11 server, for example the window id of the root window.
417    fn setup(&self) -> &Setup;
418
419    /// Generate a new X11 identifier.
420    ///
421    /// This method can, for example, be used for creating a new window. First, this method is
422    /// called to generate an identifier. Next, `xproto::create_window` can be called to
423    /// actually create the window.
424    fn generate_id(&self) -> Result<u32, ReplyOrIdError>;
425}
426
427/// Does a request have a response?
428#[derive(Debug, Copy, Clone, PartialEq, Eq)]
429pub enum RequestKind {
430    /// The request has no response, i.e. its type is "void".
431    IsVoid,
432    /// The request has a response.
433    HasResponse,
434}
435
436/// Check the request length and use BIG-REQUESTS if necessary.
437///
438/// Users of this library will most likely not want to use this function directly.
439///
440/// This function is used by implementations of `RequestConnection` for sending requests. It
441/// examines the given request buffers and checks that the length field is set correctly.
442///
443/// If the request has more than 2^18 bytes, this function handles using the BIG-REQUESTS
444/// extension. The request is rewritten to include the correct length field. For this case, the
445/// `storage` parameter is needed. This function uses it to store the necessary buffers.
446///
447/// When using this function, it is recommended to allocate the `storage` parameter with
448/// `Default::default()`.
449///
450/// Example usage:
451/// ```
452/// use std::io::IoSlice;
453/// use x11rb::connection::{BufWithFds, RequestConnection, compute_length_field};
454/// use x11rb::cookie::{Cookie, CookieWithFds, VoidCookie};
455/// use x11rb::errors::{ParseError, ConnectionError};
456/// use x11rb::utils::RawFdContainer;
457/// use x11rb::x11_utils::{ExtensionInformation, TryParse, TryParseFd};
458/// use x11rb_protocol::SequenceNumber;
459/// # use x11rb::connection::ReplyOrError;
460///
461/// struct MyConnection();
462///
463/// impl RequestConnection for MyConnection {
464///     type Buf = Vec<u8>;
465///
466///     // [snip, other functions here]
467///     # fn discard_reply(&self, sequence: SequenceNumber,
468///     #                  kind: x11rb::connection::RequestKind,
469///     #                  mode: x11rb_protocol::DiscardMode) {
470///     #    unimplemented!()
471///     # }
472///     # fn prefetch_extension_information(
473///     #     &self,
474///     #     extension_name: &'static str,
475///     # ) -> Result<(), ConnectionError> {
476///     #     unimplemented!()
477///     # }
478///     # fn extension_information(&self, ext: &'static str)
479///     # -> Result<Option<ExtensionInformation>, ConnectionError> {
480///     #    unimplemented!()
481///     # }
482///     # fn wait_for_reply_or_raw_error(&self, sequence: SequenceNumber)
483///     # -> Result<ReplyOrError<Vec<u8>>, ConnectionError> {
484///     #    unimplemented!()
485///     # }
486///     # fn wait_for_reply(&self, sequence: SequenceNumber)
487///     # -> Result<Option<Vec<u8>>, x11rb::errors::ConnectionError> {
488///     #    unimplemented!()
489///     # }
490///     # fn wait_for_reply_with_fds_raw(&self, sequence: SequenceNumber)
491///     # -> Result<ReplyOrError<BufWithFds<Vec<u8>>, Vec<u8>>, ConnectionError> {
492///     #    unimplemented!()
493///     # }
494///     # fn check_for_raw_error(&self, sequence: SequenceNumber)
495///     # ->Result<Option<Vec<u8>>, ConnectionError> {
496///     #    unimplemented!()
497///     # }
498///     # fn maximum_request_bytes(&self) -> usize {
499///     #    unimplemented!()
500///     # }
501///     # fn prefetch_maximum_request_bytes(&self) {
502///     #    unimplemented!()
503///     # }
504///     # fn parse_error(&self, _error: &[u8]) -> Result<x11rb::x11_utils::X11Error, ParseError> {
505///     #     unimplemented!()
506///     # }
507///     # fn parse_event(&self, _event: &[u8]) -> Result<x11rb::protocol::Event, ParseError> {
508///     #     unimplemented!()
509///     # }
510///
511///     fn send_request_with_reply<R>(&self, bufs: &[IoSlice], fds: Vec<RawFdContainer>)
512///     -> Result<Cookie<Self, R>, ConnectionError>
513///     where R: TryParse {
514///         Ok(Cookie::new(self, self.send_request(bufs, fds, true, false)?))
515///     }
516///
517///     fn send_request_with_reply_with_fds<R>(&self, bufs: &[IoSlice], fds: Vec<RawFdContainer>)
518///     -> Result<CookieWithFds<Self, R>, ConnectionError>
519///     where R: TryParseFd {
520///         Ok(CookieWithFds::new(self, self.send_request(bufs, fds, true, true)?))
521///     }
522///
523///     fn send_request_without_reply(&self, bufs: &[IoSlice], fds: Vec<RawFdContainer>)
524///     -> Result<VoidCookie<Self>, ConnectionError> {
525///         Ok(VoidCookie::new(self, self.send_request(bufs, fds, false, false)?))
526///     }
527/// }
528///
529/// impl MyConnection {
530///     fn send_request(&self, bufs: &[IoSlice], fds: Vec<RawFdContainer>,
531///                     has_reply: bool, reply_has_fds: bool)
532///     -> Result<SequenceNumber, ConnectionError>
533///     {
534///         let mut storage = Default::default();
535///         let bufs = compute_length_field(self, bufs, &mut storage)?;
536///         unimplemented!("Now send bufs and fds to the X11 server");
537///     }
538/// }
539/// ```
540pub fn compute_length_field<'b>(
541    conn: &impl RequestConnection,
542    request_buffers: &'b [IoSlice<'b>],
543    storage: &'b mut (Vec<IoSlice<'b>>, [u8; 8]),
544) -> Result<&'b [IoSlice<'b>], ConnectionError> {
545    // Compute the total length of the request
546    let length: usize = request_buffers.iter().map(|buf| buf.len()).sum();
547    assert_eq!(
548        length % 4,
549        0,
550        "The length of X11 requests must be a multiple of 4, got {}",
551        length
552    );
553    let wire_length = length / 4;
554
555    let first_buf = &request_buffers[0];
556
557    // If the length fits into an u16, just return the request as-is
558    if let Ok(wire_length) = u16::try_from(wire_length) {
559        // Check that the request contains the correct length field
560        let length_field = u16::from_ne_bytes([first_buf[2], first_buf[3]]);
561        assert_eq!(
562            wire_length, length_field,
563            "Length field contains incorrect value"
564        );
565        return Ok(request_buffers);
566    }
567
568    // Check that the total length is not too large
569    if length > conn.maximum_request_bytes() {
570        return Err(ConnectionError::MaximumRequestLengthExceeded);
571    }
572
573    // Okay, we need to use big requests (thus four extra bytes, "+1" below)
574    let wire_length: u32 = wire_length
575        .checked_add(1)
576        .ok_or(ConnectionError::MaximumRequestLengthExceeded)?
577        .try_into()
578        .expect("X11 request larger than 2^34 bytes?!?");
579    let wire_length = wire_length.to_ne_bytes();
580
581    // Now construct the new IoSlices
582
583    // Replacement for the first four bytes of the request
584    storage.1.copy_from_slice(&[
585        // First part of the request
586        first_buf[0],
587        first_buf[1],
588        // length field zero indicates big requests
589        0,
590        0,
591        // New bytes: extended length
592        wire_length[0],
593        wire_length[1],
594        wire_length[2],
595        wire_length[3],
596    ]);
597    storage.0.push(IoSlice::new(&storage.1));
598
599    // The remaining part of the first buffer of the request
600    storage.0.push(IoSlice::new(&first_buf[4..]));
601
602    // and the rest of the request
603    storage.0.extend(
604        request_buffers[1..]
605            .iter()
606            .map(std::ops::Deref::deref)
607            .map(IoSlice::new),
608    );
609
610    Ok(&storage.0[..])
611}