Skip to main content

ntex_io/
ioref.rs

1use std::{any, fmt, hash, io, ptr};
2
3use ntex_bytes::{BytePage, BytePages, BytesMut};
4use ntex_codec::{Decoder, Encoder};
5use ntex_service::cfg::SharedCfg;
6use ntex_util::time::Seconds;
7
8use crate::ops::{Id, Iops, TimerHandle};
9use crate::{Decoded, Filter, FilterBuf, Flags, IoConfig, IoContext, IoRef, types};
10
11impl IoRef {
12    #[inline]
13    /// Gets the ID.
14    pub fn id(&self) -> Id {
15        self.0.id()
16    }
17
18    #[inline]
19    /// Gets the I/O tag.
20    pub fn tag(&self) -> &'static str {
21        self.0.tag()
22    }
23
24    #[doc(hidden)]
25    /// Gets the current state flags.
26    pub fn flags(&self) -> Flags {
27        self.0.flags.clone()
28    }
29
30    #[inline]
31    /// Gets the current filter.
32    pub(crate) fn filter(&self) -> &dyn Filter {
33        self.0.filter()
34    }
35
36    #[inline]
37    /// Gets the configuration.
38    pub fn cfg(&self) -> &IoConfig {
39        &self.0.cfg
40    }
41
42    #[inline]
43    /// Gets the shared configuration.
44    pub fn shared(&self) -> SharedCfg {
45        self.0.cfg.shared()
46    }
47
48    #[inline]
49    /// Checks whether the I/O stream is closed.
50    pub fn is_closed(&self) -> bool {
51        self.0.flags.is_closed()
52    }
53
54    #[inline]
55    /// Checks whether write back-pressure is enabled.
56    pub fn is_wr_backpressure(&self) -> bool {
57        self.0.flags.is_wr_backpressure()
58    }
59
60    /// Gracefully closes the connection.
61    ///
62    /// Initiates the I/O stream shutdown process.
63    pub fn close(&self) {
64        self.0.start_shutdown();
65    }
66
67    /// Force-closes the connection.
68    ///
69    /// The dispatcher does not wait for incomplete responses. The I/O stream is
70    /// terminated without any graceful period.
71    pub fn terminate(&self) {
72        log::trace!("{}: Terminate io stream object", self.tag());
73        self.0.terminate_connection(None);
74    }
75
76    #[doc(hidden)]
77    #[deprecated(since = "3.10.0", note = "use IoRef::terminate() instead")]
78    /// Force close connection
79    ///
80    /// Dispatcher does not wait for uncompleted responses. Io stream get terminated
81    /// without any graceful period.
82    pub fn force_close(&self) {
83        self.terminate();
84    }
85
86    #[doc(hidden)]
87    #[deprecated(since = "3.11.0", note = "use IoRef::close() instead")]
88    /// Gracefully shuts down the I/O stream.
89    pub fn wants_shutdown(&self) {
90        self.0.start_shutdown();
91    }
92
93    /// Queries filter-specific data.
94    pub fn query<T: 'static>(&self) -> types::QueryItem<T> {
95        types::QueryItem::new(self.filter().query(any::TypeId::of::<T>()))
96    }
97
98    #[inline]
99    /// Encodes the item into the write buffer.
100    pub fn encode<U>(&self, item: U::Item, codec: &U) -> Result<(), <U as Encoder>::Error>
101    where
102        U: Encoder,
103    {
104        self.with_write_buf(|buf| codec.encodev(item, buf))
105            .unwrap_or_else(|_| Ok(()))
106    }
107
108    #[inline]
109    /// Encodes the slice into the write buffer.
110    pub fn encode_slice(&self, src: &[u8]) -> io::Result<()> {
111        self.with_write_buf(|buf| buf.extend_from_slice(src))
112    }
113
114    #[inline]
115    /// Writes bytes to the write buffer.
116    pub fn encode_bytes<B>(&self, src: B) -> io::Result<()>
117    where
118        BytePage: From<B>,
119    {
120        self.with_write_buf(|buf| buf.append(src))
121    }
122
123    /// Attempts to decode a frame from the read buffer.
124    pub fn decode<U>(
125        &self,
126        codec: &U,
127    ) -> Result<Option<<U as Decoder>::Item>, <U as Decoder>::Error>
128    where
129        U: Decoder,
130    {
131        self.0.buffer.with_read_dst(self, |buf| {
132            let res = codec.decode(buf);
133            self.0.flags.unset_read_ready();
134            self.update_read_destination(buf);
135            res
136        })
137    }
138
139    /// Attempts to decode a frame from the read buffer.
140    pub fn decode_item<U>(
141        &self,
142        codec: &U,
143    ) -> Result<Decoded<<U as Decoder>::Item>, <U as Decoder>::Error>
144    where
145        U: Decoder,
146    {
147        self.0.buffer.with_read_dst(self, |buf| {
148            let len = buf.len();
149            let res = codec.decode(buf).map(|item| Decoded {
150                item,
151                remains: buf.len(),
152                consumed: len - buf.len(),
153            });
154            self.0.flags.unset_read_ready();
155            self.update_read_destination(buf);
156            res
157        })
158    }
159
160    /// Sends the write buffer to the I/O layer.
161    ///
162    /// Requires the underlying runtime to implement `.write()`;
163    /// otherwise, no action is taken.
164    pub fn send_buf(&self) -> io::Result<()> {
165        // try send bytes
166        self.consolidate_write_state(true);
167
168        if self.0.flags.is_stopping_any()
169            && let Some(err) = self.0.error.take()
170        {
171            Err(err)
172        } else {
173            Ok(())
174        }
175    }
176
177    pub(crate) fn ops_send_buf(&self) {
178        let st = &self.0;
179        #[cfg(feature = "trace")]
180        log::trace!(
181            "{}: ops-send == buf:{} flags:{:?}",
182            st.tag(),
183            st.buffer.write_buf_size(),
184            st.flags
185        );
186
187        if st.flags.is_wr_send_scheduled() {
188            st.flags.unset_wr_send_scheduled();
189
190            if st.flags.is_write_paused() {
191                // call `Handle::write()`.
192                // if write task is not paused, io write is pending
193                // need to wake write task for io completeion
194                if self.call_write() == WakeWriteTask::Yes {
195                    st.wake_write_task();
196                    st.flags.unset_write_paused();
197                }
198            } else {
199                st.wake_write_task();
200            }
201        }
202    }
203
204    /// Get access to filter buffer
205    pub fn with_buf<F, R>(&self, f: F) -> io::Result<R>
206    where
207        F: FnOnce(&mut FilterBuf<'_>) -> R,
208    {
209        self.with_callbacks(|cb| cb.before_processing(self));
210        let result = self.0.buffer.with_filter(self, |ctx| ctx.with_buffer(f));
211        self.with_callbacks(|cb| cb.after_processing(self));
212
213        self.consolidate_write_state(false);
214        Ok(result)
215    }
216
217    /// Get mut access to read buffer
218    pub fn with_read_buf<F, R>(&self, f: F) -> R
219    where
220        F: FnOnce(&mut BytesMut) -> R,
221    {
222        self.0.buffer.with_read_dst(self, |buf| {
223            let res = f(buf);
224            self.update_read_destination(buf);
225            res
226        })
227    }
228
229    /// Get mut access to source write buffer
230    pub fn with_write_buf<F, R>(&self, f: F) -> io::Result<R>
231    where
232        F: FnOnce(&mut BytePages) -> R,
233    {
234        let st = &self.0;
235
236        if st.flags.is_stopping_any() {
237            if st.flags.is_closed() {
238                Err(st.error_or_disconnected())
239            } else {
240                Err(io::Error::other("I/O stream is closing"))
241            }
242        } else {
243            let result = st.buffer.with_write_src(f);
244            self.consolidate_write_state(false);
245            Ok(result)
246        }
247    }
248
249    #[inline]
250    /// Get mut access to src read buffer
251    pub fn with_read_src_buf<F, R>(&self, f: F) -> R
252    where
253        F: FnOnce(&mut BytesMut) -> R,
254    {
255        self.0.buffer.with_read_src(self, f)
256    }
257
258    #[inline]
259    /// Get mut access to dest write buffer
260    pub fn with_write_dst_buf<F, R>(&self, f: F) -> R
261    where
262        F: FnOnce(&mut BytePages) -> R,
263    {
264        self.0.buffer.with_write_dst(f)
265    }
266
267    pub(crate) fn consolidate_write_state(&self, force: bool) {
268        let st = &self.0;
269
270        // wake write task if needsed
271        let size = st.buffer.write_buf_size();
272
273        #[cfg(feature = "trace")]
274        log::trace!("{}: write-upd == buf:{size} flags:{:?}", st.tag(), st.flags);
275
276        if size > 0 && st.flags.is_write_paused() {
277            // The app encodes data in response to incoming data,
278            // continuing to fill the write buffer until all data
279            // has been processed. Only then can the runtime wake
280            // the write task to send the buffered data.
281            //
282            // By that time, the buffer may have accumulated a large
283            // amount of data, causing it to be sent in large bursts,
284            // which introduces latency. To prevent this behavior and
285            // flatten data delivery to the peer, IoRef can initiate
286            // out-of-order writes based on a configured threshold.
287            if st.flags.is_direct_wr_enabled()
288                && (force || size >= st.cfg.write_buf_threshold())
289            {
290                // Send data in-place
291                if self.call_write() == WakeWriteTask::Yes {
292                    #[cfg(feature = "trace")]
293                    log::trace!(
294                        "{}: write-upd == schedule(more):{} flags:{:?}",
295                        st.tag(),
296                        st.buffer.write_buf_size(),
297                        st.flags
298                    );
299                    if !st.flags.is_wr_send_scheduled() {
300                        // More data needs to be sent
301                        st.flags.set_wr_send_scheduled();
302                        Iops::schedule_write(st.id());
303                    }
304                } else {
305                    st.flags.unset_wr_send_scheduled();
306                }
307            } else if !st.flags.is_wr_send_scheduled() {
308                #[cfg(feature = "trace")]
309                log::trace!("{}: write-upd == schedule(too small)", st.tag());
310                st.flags.set_wr_send_scheduled();
311                Iops::schedule_write(st.id());
312            }
313        }
314        // Enable backpressure
315        if !st.flags.is_wr_backpressure() && st.is_wr_backpressure_needed(size) {
316            st.flags.set_wr_backpressure();
317            st.wake_dispatch_task();
318        }
319    }
320
321    fn update_read_destination(&self, buf: &mut BytesMut) {
322        let st = &self.0;
323
324        #[cfg(feature = "trace")]
325        log::trace!(
326            "{}: read-upd == buf:{} flags:{:?}",
327            st.tag(),
328            buf.len(),
329            st.flags
330        );
331
332        if st.flags.is_rd_backpressure() {
333            // back-pressure is still eanbled
334            if st.is_rd_backpressure_needed(buf.len()) {
335                return;
336            }
337            st.flags.unset_all_read_flags();
338        } else {
339            st.flags.unset_read_ready();
340        }
341
342        if st.flags.is_read_paused() {
343            st.wake_read_task();
344            st.flags.unset_read_paused();
345        }
346    }
347
348    /// Make sure buffer has enough free space
349    pub fn resize_read_buf(&self, buf: &mut BytesMut) {
350        self.0.cfg.read_buf().resize(buf);
351    }
352
353    #[doc(hidden)]
354    #[deprecated(since = "3.10.3", note = "Use .notify_disapatcher()")]
355    /// Wakeup dispatcher
356    pub fn wake(&self) {
357        self.notify_dispatcher();
358    }
359
360    /// Wakeup dispatcher
361    pub fn notify_dispatcher(&self) {
362        log::trace!("{}: Timer, notify dispatcher", self.tag());
363        self.0.wake_dispatch_task();
364    }
365
366    /// Wakeup dispatcher and send keep-alive error
367    pub fn notify_timeout(&self) {
368        self.0.notify_timeout();
369    }
370
371    /// Current timer handle
372    pub fn timer_handle(&self) -> TimerHandle {
373        self.0.timeout.get()
374    }
375
376    /// Start timer
377    pub fn start_timer(&self, timeout: Seconds) -> TimerHandle {
378        let cur_hnd = self.0.timeout.get();
379
380        if timeout.is_zero() {
381            if cur_hnd.is_set() {
382                self.0.timeout.set(TimerHandle::ZERO);
383                cur_hnd.unregister(self);
384            }
385            TimerHandle::ZERO
386        } else if cur_hnd.is_set() {
387            let hnd = cur_hnd.update(timeout, self);
388            if hnd != cur_hnd {
389                log::trace!("{}: Update timer {:?}", self.tag(), timeout);
390                self.0.timeout.set(hnd);
391            }
392            hnd
393        } else {
394            log::trace!("{}: Start timer {:?}", self.tag(), timeout);
395            let hnd = TimerHandle::register(timeout, self);
396            self.0.timeout.set(hnd);
397            hnd
398        }
399    }
400
401    /// Stop timer
402    pub fn stop_timer(&self) {
403        let hnd = self.0.timeout.get();
404        if hnd.is_set() {
405            log::trace!("{}: Stop timer", self.tag());
406            self.0.timeout.set(TimerHandle::ZERO);
407            hnd.unregister(self);
408        }
409    }
410
411    /// Notify when io stream get disconnected
412    pub fn on_disconnect(&self) -> crate::OnDisconnect {
413        crate::OnDisconnect::new(self.0.clone())
414    }
415
416    #[doc(hidden)]
417    /// Register filter callbacks
418    pub fn register_filter_callbacks<F: crate::IoCallbacks + 'static>(&self, f: F) {
419        self.0.extensions.register_filter_callbacks(f);
420    }
421
422    /// Call handle write method, returns true if
423    /// `write-paused` is still set
424    fn call_write(&self) -> WakeWriteTask {
425        if let Some(hnd) = self.0.handle.take() {
426            self.0.flags.unset_write_paused();
427            #[cfg(feature = "trace")]
428            log::trace!(
429                "{}: call-write ({}), flags:{:?}",
430                self.tag(),
431                self.0.buffer.write_buf_size(),
432                self.0.flags
433            );
434            let ctx = unsafe { &*(ptr::from_ref(self).cast::<IoContext>()) };
435            hnd.write(ctx);
436            self.0.handle.set(Some(hnd));
437        }
438        if self.0.flags.is_write_paused() {
439            WakeWriteTask::No
440        } else {
441            WakeWriteTask::Yes
442        }
443    }
444
445    pub(crate) fn call_notify(&self) {
446        if let Some(hnd) = self.0.handle.take() {
447            let ctx = unsafe { &*(ptr::from_ref(self).cast::<IoContext>()) };
448            hnd.notify(ctx);
449            self.0.handle.set(Some(hnd));
450        }
451    }
452
453    pub(crate) fn with_callbacks<F>(&self, f: F)
454    where
455        F: FnOnce(&dyn crate::IoCallbacks),
456    {
457        self.0.extensions.with_callbacks(f);
458    }
459}
460
461#[derive(Copy, Clone, PartialEq, Eq, Debug)]
462enum WakeWriteTask {
463    Yes,
464    No,
465}
466
467impl Eq for IoRef {}
468
469impl PartialEq for IoRef {
470    #[inline]
471    fn eq(&self, other: &Self) -> bool {
472        self.0.eq(&other.0)
473    }
474}
475
476impl hash::Hash for IoRef {
477    #[inline]
478    fn hash<H: hash::Hasher>(&self, state: &mut H) {
479        self.0.hash(state);
480    }
481}
482
483impl fmt::Debug for IoRef {
484    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485        f.debug_struct("IoRef")
486            .field("state", self.0.as_ref())
487            .finish()
488    }
489}
490
491#[cfg(test)]
492mod tests {
493    use std::cell::{Cell, RefCell};
494    use std::{future::Future, future::poll_fn, pin::Pin, rc::Rc, task::Poll};
495
496    use ntex_bytes::Bytes;
497    use ntex_codec::BytesCodec;
498    use ntex_util::{future::lazy, time::Millis, time::sleep};
499
500    use super::*;
501    use crate::{FilterCtx, Io, testing::IoTest};
502
503    const BIN: &[u8] = b"GET /test HTTP/1\r\n\r\n";
504    const TEXT: &str = "GET /test HTTP/1\r\n\r\n";
505
506    #[ntex::test]
507    async fn utils() {
508        let (client, server) = IoTest::create();
509        client.remote_buffer_cap(1024);
510        client.write(TEXT);
511
512        let state = Io::from(server);
513        assert_eq!(state.get_ref(), state.get_ref());
514
515        let msg = state.recv(&BytesCodec).await.unwrap().unwrap();
516        assert_eq!(msg, Bytes::from_static(BIN));
517        assert_eq!(state.get_ref(), state.as_ref().clone());
518        assert!(format!("{state:?}").find("Io {").is_some());
519        assert!(format!("{:?}", state.get_ref()).find("IoRef {").is_some());
520
521        let res = poll_fn(|cx| Poll::Ready(state.poll_recv(&BytesCodec, cx))).await;
522        assert!(res.is_pending());
523        client.write(TEXT);
524        sleep(Millis(50)).await;
525        let res = poll_fn(|cx| Poll::Ready(state.poll_recv(&BytesCodec, cx))).await;
526        if let Poll::Ready(msg) = res {
527            assert_eq!(msg.unwrap(), Bytes::from_static(BIN));
528        }
529
530        client.read_error(io::Error::other("err"));
531        let msg = state.recv(&BytesCodec).await;
532        assert!(msg.is_err());
533        assert!(state.flags().is_terminated());
534
535        let (client, server) = IoTest::create();
536        client.remote_buffer_cap(1024);
537        let state = Io::from(server);
538
539        client.read_error(io::Error::other("err"));
540        let res = poll_fn(|cx| Poll::Ready(state.poll_recv(&BytesCodec, cx))).await;
541        if let Poll::Ready(msg) = res {
542            assert!(msg.is_err());
543            assert!(state.flags().is_terminated());
544        }
545
546        let (client, server) = IoTest::create();
547        client.remote_buffer_cap(1024);
548        let state = Io::from(server);
549        assert_eq!(0, state.with_write_dst_buf(|b| b.len()));
550        state.encode_slice(b"test").unwrap();
551        assert_eq!(4, state.with_write_dst_buf(|b| b.len()));
552        let buf = client.read().await.unwrap();
553        assert_eq!(buf, Bytes::from_static(b"test"));
554
555        client.write(b"test");
556        state.read_ready().await.unwrap();
557        let buf = state.decode(&BytesCodec).unwrap().unwrap();
558        assert_eq!(buf, Bytes::from_static(b"test"));
559
560        client.write_error(io::Error::other("err"));
561        state
562            .send(Bytes::from_static(b"test"), &BytesCodec)
563            .await
564            .unwrap();
565        assert!(state.flags().is_terminated());
566
567        let res = state.send(Bytes::from_static(b"test"), &BytesCodec).await;
568        assert!(res.is_err());
569
570        let (client, server) = IoTest::create();
571        client.remote_buffer_cap(1024);
572        let state = Io::from(server);
573        state.terminate();
574        assert!(state.flags().is_stopping());
575        assert!(state.flags().is_terminated());
576    }
577
578    #[ntex::test]
579    #[allow(clippy::unit_cmp)]
580    async fn on_disconnect() {
581        let (client, server) = IoTest::create();
582        let state = Io::from(server);
583        let mut waiter = state.on_disconnect();
584        assert_eq!(
585            lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
586            Poll::Pending
587        );
588        let mut waiter2 = waiter.clone();
589        assert_eq!(
590            lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await,
591            Poll::Pending
592        );
593        client.close().await;
594        assert_eq!(waiter.await, ());
595        assert_eq!(waiter2.await, ());
596
597        let mut waiter = state.on_disconnect();
598        assert_eq!(
599            lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
600            Poll::Ready(())
601        );
602
603        let (client, server) = IoTest::create();
604        let state = Io::from(server);
605        let mut waiter = state.on_disconnect();
606        assert_eq!(
607            lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
608            Poll::Pending
609        );
610        client.read_error(io::Error::other("err"));
611        assert_eq!(waiter.await, ());
612    }
613
614    #[ntex::test]
615    async fn write_to_closed_io() {
616        let (client, server) = IoTest::create();
617        let state = Io::from(server);
618        client.close().await;
619
620        assert!(state.is_closed());
621        assert!(state.encode_slice(TEXT.as_bytes()).is_err());
622        assert!(state.encode_bytes(Bytes::from_static(BIN)).is_err());
623        assert!(
624            state
625                .with_write_buf(|buf| buf.extend_from_slice(BIN))
626                .is_err()
627        );
628    }
629
630    #[derive(Debug)]
631    struct Counter<F> {
632        layer: F,
633        idx: usize,
634        in_bytes: Rc<Cell<usize>>,
635        out_bytes: Rc<Cell<usize>>,
636        read_order: Rc<RefCell<Vec<usize>>>,
637        write_order: Rc<RefCell<Vec<usize>>>,
638    }
639
640    impl<F: Filter> Filter for Counter<F> {
641        fn process_read_buf(&self, ctx: &mut FilterCtx<'_>) -> io::Result<()> {
642            self.read_order.borrow_mut().push(self.idx);
643            let result = self.layer.process_read_buf(ctx);
644            self.in_bytes
645                .set(self.in_bytes.get() + ctx.new_read_bytes());
646            result
647        }
648
649        fn process_write_buf(&self, ctx: &mut FilterCtx<'_>) -> io::Result<()> {
650            self.write_order.borrow_mut().push(self.idx);
651            ctx.with_buffer(|buf| {
652                buf.with_write_buffers(|src, _| {
653                    self.out_bytes.set(self.out_bytes.get() + src.len());
654                });
655            });
656            self.layer.process_write_buf(ctx)
657        }
658
659        crate::forward_ready!(layer);
660        crate::forward_query!(layer);
661        crate::forward_shutdown!(layer);
662    }
663
664    #[ntex::test]
665    async fn filter() {
666        let in_bytes = Rc::new(Cell::new(0));
667        let out_bytes = Rc::new(Cell::new(0));
668        let read_order = Rc::new(RefCell::new(Vec::new()));
669        let write_order = Rc::new(RefCell::new(Vec::new()));
670
671        let (client, server) = IoTest::create();
672        let io = Io::from(server)
673            .map_filter(|layer| Counter {
674                layer,
675                idx: 1,
676                in_bytes: in_bytes.clone(),
677                out_bytes: out_bytes.clone(),
678                read_order: read_order.clone(),
679                write_order: write_order.clone(),
680            })
681            .seal();
682
683        client.remote_buffer_cap(1024);
684        client.write(TEXT);
685        let msg = io.recv(&BytesCodec).await.unwrap().unwrap();
686        assert_eq!(msg, Bytes::from_static(BIN));
687
688        io.send(Bytes::from_static(b"test"), &BytesCodec)
689            .await
690            .unwrap();
691        let buf = client.read().await.unwrap();
692        assert_eq!(buf, Bytes::from_static(b"test"));
693
694        client.write(TEXT);
695        let msg = io.recv(&BytesCodec).await.unwrap().unwrap();
696        assert_eq!(msg, Bytes::from_static(BIN));
697
698        assert_eq!(in_bytes.get(), BIN.len() * 2);
699        assert_eq!(out_bytes.get(), 8);
700    }
701
702    #[ntex::test]
703    async fn boxed_filter() {
704        let in_bytes = Rc::new(Cell::new(0));
705        let out_bytes = Rc::new(Cell::new(0));
706        let read_order = Rc::new(RefCell::new(Vec::new()));
707        let write_order = Rc::new(RefCell::new(Vec::new()));
708
709        let (client, server) = IoTest::create();
710        let state = Io::from(server)
711            .map_filter(|layer| Counter {
712                layer,
713                idx: 2,
714                in_bytes: in_bytes.clone(),
715                out_bytes: out_bytes.clone(),
716                read_order: read_order.clone(),
717                write_order: write_order.clone(),
718            })
719            .map_filter(|layer| Counter {
720                layer,
721                idx: 1,
722                in_bytes: in_bytes.clone(),
723                out_bytes: out_bytes.clone(),
724                read_order: read_order.clone(),
725                write_order: write_order.clone(),
726            });
727        let state = state.seal();
728
729        client.remote_buffer_cap(1024);
730        client.write(TEXT);
731        let msg = state.recv(&BytesCodec).await.unwrap().unwrap();
732        assert_eq!(msg, Bytes::from_static(BIN));
733
734        state
735            .send(Bytes::from_static(b"test"), &BytesCodec)
736            .await
737            .unwrap();
738        let buf = client.read().await.unwrap();
739        assert_eq!(buf, Bytes::from_static(b"test"));
740
741        assert_eq!(in_bytes.get(), BIN.len() * 2);
742        assert_eq!(out_bytes.get(), 16);
743        assert_eq!(state.0.buffer.with_write_dst(|b| b.len()), 0);
744
745        // refs
746        assert_eq!(Rc::strong_count(&in_bytes), 3);
747        drop(state);
748        assert_eq!(Rc::strong_count(&in_bytes), 1);
749        assert_eq!(*read_order.borrow(), &[1, 2][..]);
750        assert_eq!(*write_order.borrow(), &[1, 2, 1, 2, 1, 2][..]);
751    }
752}