z_serial/
lib.rs

1//
2// Copyright (c) 2022 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15use std::{path::Path, time::Duration};
16use tokio::io::{AsyncReadExt, AsyncWriteExt};
17use tokio_serial::{ClearBuffer, SerialPort, SerialPortBuilderExt, SerialStream};
18
19pub const MAX_FRAME_SIZE: usize = 1510;
20pub const MAX_MTU: usize = 1500;
21
22const CRC32_LEN: usize = 4;
23
24// Given by cobs::max_encoding_length(MAX_FRAME_SIZE)
25const COBS_BUF_SIZE: usize = 1517;
26
27const LEN_FIELD_LEN: usize = 2;
28
29const KIND_FIELD_LEN: usize = 1;
30
31const SENTINEL: u8 = 0x00;
32
33const CRC_TABLE_SIZE: usize = 256;
34
35const POLYNOMIA: u32 = 0x04C11DB7;
36
37const SERIAL_CONNECT_THROTTLE_TIME_US: u64 = 250_000;
38
39/// ZSerial Frame Format
40///
41/// Using COBS
42///
43/// +-+-+----+------------+--------+-+
44/// |O|H|XXXX|ZZZZ....ZZZZ|CCCCCCCC|0|
45/// +-+----+------------+--------+-+
46/// |O| |Len |   Data     |  CRC32 |C|
47/// +-+-+-2--+----N-------+---4----+-+
48///
49/// Header: 1byte
50/// +---------------+
51/// |7|6|5|4|3|2|1|0|
52/// +---------------+
53/// |x|x|x|x|x|R|A|I|
54/// +---------------+
55///
56/// Flags:
57/// I - Init
58/// A - Ack
59/// R - Reset
60///
61/// Max Frame Size: 1510
62/// Max MTU: 1500
63/// Max On-the-wire length: 1516 (MFS + Overhead Byte (OHB) + Kind Byte + End of packet (EOP))
64
65/// Initializaiton message exchange from cold start
66///
67/// 1) Connect side sends a message with empty payload and I flag set.
68/// 2) Listen side sends a message with empty payload and I+A flags set.
69/// 3) Data can be exchanged from this point on.
70///
71///  ┌─────────┐                ┌──────────┐   
72///  │ Listen  │                │ Connect  │   
73///  └────┬────┘                └────┬─────┘   
74///       │                          │         
75///       │       Init               │         
76///       │◄─────────────────────────┤         
77///       │                          │         
78///       │        Init + Ack        │         
79///       ├─────────────────────────►│         
80///       │       Data               │         
81///       │◄─────────────────────────┤         
82///       ├─────────────────────────►│         
83///       │                          │         
84///
85/// If connect sides restarts:
86///
87/// 1) Connect side sends a message with empty payload and I flag set.   
88/// 2) Listen side sends a message with empty payload and R flag set.
89/// 3) Connect side waits and goes back to cold start case.
90///                                                                                                
91///   ┌─────────┐                ┌──────────┐
92///   │ Listen  │                │ Connect  │
93///   └────┬────┘                └────┬─────┘
94///        │        Init              │       
95///        │◄─────────────────────────┤       
96///        │       Reset              │       
97///        ├─────────────────────────►│       
98///        │                          │       
99///        │                          │       
100///        │                          │       
101///        │        Init              │       
102///        │◄─────────────────────────┤       
103///        │       Init + Ack         │       
104///        ├─────────────────────────►│       
105///        │         Data             │       
106///        │◄─────────────────────────┤       
107///        ├─────────────────────────►│       
108///        │                          │       
109///        │                          │       
110
111const I_FLAG: u8 = 0x01;
112const A_FLAG: u8 = 0x02;
113const R_FLAG: u8 = 0x04;
114
115#[derive(Debug)]
116struct Header(u8);
117
118impl Header {
119    pub fn new(flags: u8) -> Self {
120        Header(flags)
121    }
122
123    pub fn has_i_flag(&self) -> bool {
124        self.0 & I_FLAG == I_FLAG
125    }
126
127    pub fn has_a_flag(&self) -> bool {
128        self.0 & A_FLAG == A_FLAG
129    }
130
131    pub fn has_r_flag(&self) -> bool {
132        self.0 & R_FLAG == R_FLAG
133    }
134
135    pub fn get_byte(&self) -> u8 {
136        self.0
137    }
138}
139
140impl From<u8> for Header {
141    fn from(value: u8) -> Self {
142        Self(value)
143    }
144}
145
146#[derive(Debug)]
147pub struct CRC32 {
148    table: [u32; CRC_TABLE_SIZE],
149}
150
151impl CRC32 {
152    pub fn compute_crc32(&self, buff: &[u8]) -> u32 {
153        let mut acc: u32 = !0;
154
155        for b in buff {
156            let octect = *b;
157            acc = (acc >> 8) ^ self.table[((acc & 0xFF) ^ octect as u32) as usize]
158        }
159
160        !acc
161    }
162}
163
164impl Default for CRC32 {
165    fn default() -> Self {
166        let mut table = [0u32; CRC_TABLE_SIZE];
167        for n in 0..256 {
168            let mut rem = n;
169
170            for _ in 0..8 {
171                match rem & 1 {
172                    1 => rem = POLYNOMIA ^ (rem >> 1),
173                    _ => rem >>= 1,
174                }
175            }
176
177            table[n as usize] = rem;
178        }
179
180        Self { table }
181    }
182}
183
184#[derive(Debug)]
185struct WireFormat {
186    buff: Vec<u8>,
187    crc: CRC32,
188}
189
190impl WireFormat {
191    pub(crate) fn new() -> Self {
192        // Generating CRC table
193        let crc = CRC32::default();
194
195        Self {
196            buff: vec![0u8; COBS_BUF_SIZE],
197            crc,
198        }
199    }
200
201    pub(crate) fn serialize_into(
202        &mut self,
203        src: &[u8],
204        dest: &mut [u8],
205        header: Header,
206    ) -> tokio_serial::Result<usize> {
207        if src.len() > MAX_MTU {
208            return Err(tokio_serial::Error::new(
209                tokio_serial::ErrorKind::InvalidInput,
210                "Payload is too big",
211            ));
212        }
213
214        // Compute CRC
215        let crc32 = self.crc.compute_crc32(src).to_ne_bytes();
216
217        // Compute wise_size
218        let wire_size: u16 = src.len() as u16;
219
220        let size_bytes = wire_size.to_ne_bytes();
221
222        // Copy into serialization buffer
223        self.buff[0] = header.get_byte();
224        self.buff[KIND_FIELD_LEN..LEN_FIELD_LEN + KIND_FIELD_LEN].copy_from_slice(&size_bytes);
225        self.buff[LEN_FIELD_LEN + KIND_FIELD_LEN..LEN_FIELD_LEN + KIND_FIELD_LEN + src.len()]
226            .copy_from_slice(src);
227        self.buff[LEN_FIELD_LEN + KIND_FIELD_LEN + src.len()
228            ..LEN_FIELD_LEN + KIND_FIELD_LEN + src.len() + CRC32_LEN]
229            .copy_from_slice(&crc32);
230
231        let total_len = KIND_FIELD_LEN + LEN_FIELD_LEN + CRC32_LEN + src.len();
232
233        log::trace!(
234            "Frame before COBS encoding {:02X?}",
235            &self.buff[0..total_len]
236        );
237
238        // COBS encode
239        let mut written = cobs::encode_with_sentinel(&self.buff[0..total_len], dest, SENTINEL);
240
241        // Add sentinel byte, marks the end of a message
242        dest[written] = SENTINEL;
243        written += 1;
244
245        Ok(written)
246    }
247
248    pub(crate) fn deserialize_into(
249        &self,
250        src: &mut [u8],
251        dst: &mut [u8],
252    ) -> tokio_serial::Result<(usize, Header)> {
253        let decoded_size = cobs::decode_in_place_with_sentinel(src, SENTINEL).map_err(|e| {
254            tokio_serial::Error::new(
255                tokio_serial::ErrorKind::InvalidInput,
256                format!("Unable COBS decode: {e:?}"),
257            )
258        })?;
259
260        log::trace!("Frame after COBS encoding {:02X?}", &src[0..decoded_size]);
261
262        // Check if message has the minimum size
263        if decoded_size < LEN_FIELD_LEN + CRC32_LEN {
264            return Err(tokio_serial::Error::new(
265                tokio_serial::ErrorKind::InvalidInput,
266                "Serial is smaller than the minimum size",
267            ));
268        }
269
270        // Decoding message kind
271        let hdr = Header::from(src[0]);
272        // Decoding message size
273        let wire_size = ((src[2] as u16) << 8 | src[1] as u16) as usize;
274
275        // Check if the frame size is correct
276        if KIND_FIELD_LEN + LEN_FIELD_LEN + wire_size + CRC32_LEN != decoded_size {
277            return Err(tokio_serial::Error::new(
278                tokio_serial::ErrorKind::InvalidInput,
279                "Payload does not match the its size",
280            ));
281        }
282
283        // Getting the data
284        let data = &src[KIND_FIELD_LEN + LEN_FIELD_LEN..KIND_FIELD_LEN + wire_size + LEN_FIELD_LEN];
285
286        let crc_received_bytes = &src[KIND_FIELD_LEN + LEN_FIELD_LEN + wire_size
287            ..KIND_FIELD_LEN + LEN_FIELD_LEN + wire_size + CRC32_LEN];
288
289        let recv_crc: u32 = ((crc_received_bytes[3] as u32) << 24)
290            | ((crc_received_bytes[2] as u32) << 16)
291            | ((crc_received_bytes[1] as u32) << 8)
292            | (crc_received_bytes[0] as u32);
293
294        // Compute CRC locally
295        let computed_crc = self.crc.compute_crc32(&data[0..wire_size]);
296
297        log::trace!("Received CRC {recv_crc:02X?}  Computed CRC {computed_crc:02X?}");
298
299        // Check CRC
300        if recv_crc != computed_crc {
301            return Err(tokio_serial::Error::new(
302                tokio_serial::ErrorKind::InvalidInput,
303                format!(
304                    "CRC does not match Received {:02X?} Computed {:02X?}",
305                    recv_crc, computed_crc
306                ),
307            ));
308        }
309
310        // Copy into user slice.
311        dst[0..wire_size].copy_from_slice(data);
312
313        Ok((wire_size, hdr))
314    }
315}
316
317#[derive(PartialEq, Eq)]
318enum Status {
319    Uninitialized,
320    Initialized,
321}
322
323pub struct ZSerial {
324    port: String,
325    baud_rate: u32,
326    serial: SerialStream,
327    send_buff: Vec<u8>,
328    recv_buff: Vec<u8>,
329    formatter: WireFormat,
330    status: Status,
331}
332
333
334#[cfg(windows)]
335unsafe impl Send for ZSerial {}
336
337#[cfg(windows)]
338unsafe impl Sync for ZSerial {}
339
340impl ZSerial {
341    pub fn new(port: String, baud_rate: u32, exclusive: bool) -> tokio_serial::Result<Self> {
342        let mut serial = tokio_serial::new(port.clone(), baud_rate).open_native_async()?;
343
344        #[cfg(unix)]
345        serial.set_exclusive(exclusive)?;
346        serial.clear(ClearBuffer::All)?;
347
348        Ok(Self {
349            port,
350            baud_rate,
351            serial,
352            send_buff: vec![0u8; COBS_BUF_SIZE],
353            recv_buff: vec![0u8; COBS_BUF_SIZE],
354            formatter: WireFormat::new(),
355            status: Status::Uninitialized,
356        })
357    }
358
359    pub fn close(&mut self) {
360        self.status = Status::Uninitialized;
361    }
362
363    pub async fn accept(&mut self) -> tokio_serial::Result<()> {
364        if self.status == Status::Initialized {
365            return Err(tokio_serial::Error {
366                kind: tokio_serial::ErrorKind::InvalidInput,
367                description: "Cannot accept on an intialized connection!".into(),
368            });
369        }
370
371        log::trace!("Waiting for connection");
372        let mut buff = vec![0u8; COBS_BUF_SIZE];
373
374        // Clear all buffers
375        self.clear()?;
376
377        loop {
378            // wait for an empty message with I flag
379
380            let (_read, hdr) = self.internal_read(&mut buff).await?;
381            log::trace!("Received header: {hdr:02X?}");
382            if hdr.has_i_flag() {
383                // we send back a message with both I and A flags
384                self.internal_write(&[], Header::new(I_FLAG | A_FLAG))
385                    .await?;
386
387                // we must set our internal status to initialized
388                self.status = Status::Initialized;
389                return Ok(());
390            } // otherwise ignore
391        }
392    }
393
394    pub async fn connect(&mut self, tout: Option<Duration>) -> tokio_serial::Result<()> {
395        let tout = tout.unwrap_or(Duration::from_micros(SERIAL_CONNECT_THROTTLE_TIME_US));
396        let mut buff = vec![0u8; COBS_BUF_SIZE];
397
398        // we must first send a en emtpy message with I flag
399        loop {
400            let hdr = Header::new(I_FLAG);
401            log::trace!("Sending {hdr:02X?}");
402            self.internal_write(&[], hdr).await?;
403
404            // we then wait for a message with both I and A flags
405            let (_read, hdr) = self.internal_read(&mut buff).await?;
406            log::trace!("Received header: {hdr:02X?}");
407
408            if hdr.has_a_flag() && hdr.has_i_flag() {
409                // correct initialiation
410                self.status = Status::Initialized;
411                break;
412            } else if hdr.has_r_flag() {
413                // we received a reset we must resend the init message after a small sleep
414                tokio::time::sleep(tout).await;
415                continue;
416            } else {
417                return Err(tokio_serial::Error {
418                    kind: tokio_serial::ErrorKind::InvalidInput,
419                    description: format!("Unknown header: {hdr:02X?}"),
420                });
421            }
422        }
423
424        Ok(())
425    }
426
427    pub async fn dump(&mut self) -> tokio_serial::Result<()> {
428        self.serial
429            .read_exact(std::slice::from_mut(&mut self.recv_buff[0]))
430            .await?;
431        println!("Read {:02X?}", self.recv_buff[0]);
432        Ok(())
433    }
434
435    async fn check_device(&self) -> tokio_serial::Result<()> {
436        // check if the file is still there
437        if tokio::fs::metadata(self.port.clone()).await.is_err() {
438            // the file does not exist anymore returing an error
439            return Err(tokio_serial::Error::new(
440                tokio_serial::ErrorKind::NoDevice,
441                "Serial device disappeared".to_string(),
442            ));
443        }
444        Ok(())
445    }
446    async fn internal_read(&mut self, buff: &mut [u8]) -> tokio_serial::Result<(usize, Header)> {
447        //check if the device is sitll there
448        self.check_device().await?;
449
450        let mut start_count = 0;
451
452        if buff.len() < MAX_MTU {
453            return Err(tokio_serial::Error::new(
454                tokio_serial::ErrorKind::InvalidInput,
455                format!("Recv buffer is too small, required minimum {MAX_MTU}"),
456            ));
457        }
458
459        // Read
460        loop {
461            // Check if we are reading too much, maybe we lost the sentinel.
462            if start_count == COBS_BUF_SIZE {
463                return Ok((0, Header::new(0u8)));
464            }
465
466            // Read one byte at time until we reach the sentinel
467            self.serial
468                .read_exact(std::slice::from_mut(&mut self.recv_buff[start_count]))
469                .await?;
470
471            if self.recv_buff[start_count] == SENTINEL {
472                break;
473            }
474            start_count += 1;
475        }
476
477        start_count += 1;
478
479        log::trace!(
480            "Read {start_count} bytes COBS {:02X?}",
481            &self.recv_buff[0..start_count]
482        );
483
484        // Deserialize
485        self.formatter
486            .deserialize_into(&mut self.recv_buff[0..start_count], buff)
487    }
488
489    pub async fn read_msg(&mut self, buff: &mut [u8]) -> tokio_serial::Result<usize> {
490        let (read, hdr) = self.internal_read(buff).await?;
491        //TODO: check the kind is the expected one.
492        if self.status == Status::Initialized && hdr.has_i_flag() {
493            // we must rest the connection here
494            self.internal_write(&[], Header::new(R_FLAG)).await?;
495            self.status = Status::Uninitialized;
496            return Err(tokio_serial::Error {
497                kind: tokio_serial::ErrorKind::InvalidInput,
498                description: "Unexpected Init flag in message".into(),
499            });
500        }
501
502        Ok(read)
503    }
504
505    #[allow(dead_code)]
506    async fn read(serial: &mut SerialStream, buff: &mut [u8]) -> tokio_serial::Result<usize> {
507        Ok(serial.read(buff).await?)
508    }
509
510    #[allow(dead_code)]
511    async fn read_all(serial: &mut SerialStream, buff: &mut [u8]) -> tokio_serial::Result<()> {
512        let mut read: usize = 0;
513        while read < buff.len() {
514            let n = Self::read(serial, &mut buff[read..]).await?;
515            read += n;
516        }
517        Ok(())
518    }
519
520    async fn internal_write(&mut self, buff: &[u8], hdr: Header) -> tokio_serial::Result<()> {
521        //check if the device is sitll there
522        self.check_device().await?;
523
524        // Serialize
525        let written = self
526            .formatter
527            .serialize_into(buff, &mut self.send_buff, hdr)?;
528
529        log::trace!(
530            "Wrote {written}bytes COBS {:02X?}",
531            &self.send_buff[0..written]
532        );
533
534        // Write
535        self.serial.write_all(&self.send_buff[0..written]).await?;
536        self.serial.flush().await?;
537        Ok(())
538    }
539
540    pub async fn write(&mut self, buff: &[u8]) -> tokio_serial::Result<()> {
541        self.internal_write(buff, Header::new(0u8)).await
542    }
543
544    /// Gets the configured baud rate
545    pub fn baud_rate(&self) -> u32 {
546        self.baud_rate
547    }
548
549    /// Gets the configured serial port
550    pub fn port(&self) -> String {
551        self.port.clone()
552    }
553
554    pub fn bytes_to_read(&self) -> tokio_serial::Result<u32> {
555        self.serial.bytes_to_read()
556    }
557
558    pub fn clear(&self) -> tokio_serial::Result<()> {
559        self.serial.clear(ClearBuffer::All)
560    }
561}
562
563pub fn get_available_port_names() -> tokio_serial::Result<Vec<String>> {
564    let port_names: Vec<String> = tokio_serial::available_ports()?
565        .iter()
566        .map(|info| {
567            Path::new(&info.port_name)
568                .file_name()
569                .map(|os_str| os_str.to_string_lossy().to_string())
570                .ok_or_else(|| {
571                    tokio_serial::Error::new(
572                        tokio_serial::ErrorKind::Unknown,
573                        "Unsupported port name",
574                    )
575                })
576        })
577        .collect::<Result<Vec<String>, _>>()?;
578
579    Ok(port_names)
580}
581
582#[cfg(test)]
583mod tests {
584    use crate::Header;
585
586    use super::{WireFormat, COBS_BUF_SIZE};
587
588    #[test]
589    fn test_ser() {
590        let mut formatter = WireFormat::new();
591        let mut ser_buff = vec![0u8; COBS_BUF_SIZE];
592
593        let data: Vec<u8> = vec![0x00, 0x11, 0x00];
594
595        // COBS encoded 0x00 | 0x03 0x00 | 0x00 0x11 0x00 | 0x73 0xEC 0x75 0xF9 |
596        //              Hdr |   Len     |   Data         |      CRC32          |
597        let serialzed_data: Vec<u8> = vec![
598            0x01, 0x02, 0x03, 0x01, 0x02, 0x11, 0x05, 0x73, 0xEC, 0x75, 0xF9, 0x00,
599        ];
600
601        // Checks serialization
602        let written = formatter
603            .serialize_into(&data, &mut ser_buff, Header::new(0u8))
604            .unwrap();
605        assert_eq!(written, serialzed_data.len());
606        assert_eq!(serialzed_data, ser_buff[0..written]);
607
608        //2nd Check
609
610        let data: Vec<u8> = vec![0x11, 0x22, 0x00, 0x33];
611
612        // COBS encoded 0x00 | 0x04 0x00 | 0x11 0x22 0x00 0x33 | 0x8D 0x03 0x6D 0xFB |
613        //              Hdr |   Len     |   Data              |      CRC32          |
614        let serialzed_data: Vec<u8> = vec![
615            0x01, 0x02, 0x04, 0x03, 0x11, 0x22, 0x06, 0x33, 0x8D, 0x03, 0x6D, 0xFB, 0x00,
616        ];
617
618        let written = formatter
619            .serialize_into(&data, &mut ser_buff, Header::new(0u8))
620            .unwrap();
621        assert_eq!(written, serialzed_data.len());
622        assert_eq!(serialzed_data, ser_buff[0..written]);
623    }
624
625    #[test]
626    fn test_de() {
627        let formatter = WireFormat::new();
628        let mut buff = vec![0u8; COBS_BUF_SIZE];
629
630        let data: Vec<u8> = vec![0x00, 0x11, 0x00];
631        // COBS encoded 0x01 | 0x03 0x00 | 0x00 0x11 0x00 | 0x73 0xEC 0x75 0xF9 |
632        //              Hdr |   Len     |   Data         |      CRC32          |
633        let mut serialzed_data: Vec<u8> = vec![
634            0x01, 0x02, 0x03, 0x01, 0x02, 0x11, 0x05, 0x73, 0xEC, 0x75, 0xF9, 0x00,
635        ];
636        let serialized_len = serialzed_data.len();
637
638        let (read, hdr) = formatter
639            .deserialize_into(&mut serialzed_data[0..serialized_len], &mut buff)
640            .unwrap();
641
642        assert_eq!(read, data.len());
643        assert!(!hdr.has_i_flag());
644        assert_eq!(buff[0..read], data);
645
646        //2nd Check
647
648        let data: Vec<u8> = vec![0x11, 0x22, 0x00, 0x33];
649
650        // COBS encoded 0x02 | 0x04 0x00 | 0x11 0x22 0x00 0x33 | 0x8D 0x03 0x6D 0xFB |
651        //              hdr |   Len     |   Data              |      CRC32          |
652        let mut serialzed_data: Vec<u8> = vec![
653            0x01, 0x02, 0x04, 0x03, 0x11, 0x22, 0x06, 0x33, 0x8D, 0x03, 0x6D, 0xFB, 0x00,
654        ];
655        let serialized_len = serialzed_data.len();
656
657        let (read, hdr) = formatter
658            .deserialize_into(&mut serialzed_data[0..serialized_len], &mut buff)
659            .unwrap();
660
661        assert_eq!(read, data.len());
662        assert_eq!(buff[0..read], data);
663        assert!(!hdr.has_i_flag());
664    }
665
666    #[test]
667    fn test_serde_one_byte() {
668        let mut formatter = WireFormat::new();
669        let mut ser_buff = vec![0u8; COBS_BUF_SIZE];
670        let mut de_buff = vec![0u8; COBS_BUF_SIZE];
671
672        let data: Vec<u8> = vec![0x00];
673        let written = formatter
674            .serialize_into(&data, &mut ser_buff, Header::new(0u8))
675            .unwrap();
676
677        println!("Data: {data:02X?}");
678        println!("Serialized: {:02X?}", &ser_buff[0..written]);
679
680        let (read, hdr) = formatter
681            .deserialize_into(&mut ser_buff[0..written], &mut de_buff)
682            .unwrap();
683
684        println!("Deserialized: {:02X?}", &de_buff[0..read]);
685
686        assert_eq!(read, data.len());
687        assert!(!hdr.has_i_flag());
688
689        assert_eq!(data, de_buff[0..read]);
690    }
691
692    #[test]
693    fn test_serde_emtpy() {
694        let mut formatter = WireFormat::new();
695        let mut ser_buff = vec![0u8; COBS_BUF_SIZE];
696        let mut de_buff = vec![0u8; COBS_BUF_SIZE];
697
698        let data: Vec<u8> = vec![];
699        let written = formatter
700            .serialize_into(&data, &mut ser_buff, Header::new(0u8))
701            .unwrap();
702
703        println!("Data: {data:02X?}");
704        println!("Serialized: {:02X?}", &ser_buff[0..written]);
705
706        let (read, hdr) = formatter
707            .deserialize_into(&mut ser_buff[0..written], &mut de_buff)
708            .unwrap();
709
710        println!("Deserialized: {:02X?}", &de_buff[0..read]);
711
712        assert_eq!(read, data.len());
713        assert!(!hdr.has_i_flag());
714
715        assert_eq!(data, de_buff[0..read]);
716    }
717}