├── .gitignore ├── Cargo.toml ├── .travis.yml ├── LICENSE-MIT ├── README.md ├── src ├── error.rs ├── lib.rs ├── decoder.rs └── encoder.rs └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Matt Vertescher "] 3 | categories = ["no-std"] 4 | description = "Serial Line Internet Protocol (SLIP) API." 5 | documentation = "https://docs.rs/serial-line-ip" 6 | homepage = "https://github.com/mvertescher/serial-line-ip-rs" 7 | license = "MIT OR Apache-2.0" 8 | name = "serial-line-ip" 9 | readme = "README.md" 10 | repository = "https://github.com/mvertescher/serial-line-ip-rs" 11 | version = "0.5.1-alpha.0" 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | matrix: 4 | include: 5 | - os: linux 6 | rust: stable 7 | env: TARGET=x86_64-unknown-linux-gnu 8 | - os: linux 9 | rust: nightly 10 | env: TARGET=x86_64-unknown-linux-gnu 11 | env: 12 | global: 13 | - PROJECT_NAME: serial-line-ip 14 | - RUST_BACKTRACE: full 15 | script: 16 | - cargo build --verbose --all 17 | - cargo test --verbose --all 18 | - cargo doc --verbose --all 19 | notifications: 20 | email: 21 | on_success: never 22 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Matt Vertescher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serial-line-ip 2 | [![Build Status](https://travis-ci.com/mvertescher/serial-line-ip-rs.svg?branch=master)](https://travis-ci.com/mvertescher/serial-line-ip-rs) 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 5 | [![Crates.io - serial-line-ip](https://img.shields.io/crates/v/serial-line-ip.svg)](https://crates.io/crates/serial-line-ip) 6 | [![Released API docs](https://docs.rs/serial-line-ip/badge.svg)](https://docs.rs/serial-line-ip) 7 | 8 | Serial Line Internet Protocol (SLIP) implementation. 9 | 10 | ## License 11 | 12 | Licensed under either of 13 | 14 | * Apache License, Version 2.0 15 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 16 | * MIT license 17 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 18 | 19 | at your option. 20 | 21 | ## Contribution 22 | 23 | Unless you explicitly state otherwise, any contribution intentionally submitted 24 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 25 | dual licensed as above, without any additional terms or conditions. 26 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Possible SLIP encoding and decoding errors 2 | 3 | use core::fmt; 4 | 5 | /// Type alias for handling SLIP errors. 6 | pub type Result = core::result::Result; 7 | 8 | /// Errors encountered by SLIP. 9 | #[derive(Debug)] 10 | pub enum Error { 11 | // Encoder errors 12 | /// The encoder does not have enough space to write the SLIP header. 13 | NoOutputSpaceForHeader, 14 | /// The encoder does not have enough space to write the final SLIP end byte. 15 | NoOutputSpaceForEndByte, 16 | 17 | // Decoder errors 18 | /// The decoder cannot process the SLIP header. 19 | BadHeaderDecode, 20 | /// The decoder cannot process the SLIP escape sequence. 21 | BadEscapeSequenceDecode, 22 | } 23 | 24 | impl fmt::Display for Error { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | f.write_str(match self { 27 | Error::NoOutputSpaceForHeader => "insufficient space in output buffer for header", 28 | Error::NoOutputSpaceForEndByte => "insufficient space in output buffer for end byte", 29 | Error::BadHeaderDecode => "malformed header", 30 | Error::BadEscapeSequenceDecode => "malformed escape sequence", 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Serial Line Internet Protocol (SLIP) 2 | //! 3 | //! Pure Rust implementation of [RFC 1055](https://tools.ietf.org/html/rfc1055) 4 | //! Serial Line IP. 5 | //! 6 | //! ## What is SLIP 7 | //! 8 | //! SLIP is a very simple packet framing protocol. It is used to convert streams of 9 | //! bytes into frames and vice versa. It has no addressing, packet types, error 10 | //! correction or compression. SLIP just solves the problem of framing arbitrary 11 | //! sized data streams! 12 | //! 13 | //! ## Examples 14 | //! 15 | //! SLIP can be used to both encode and decode streams of bytes: 16 | //! 17 | //! ### Encoding 18 | //! 19 | //! The SLIP encoder can process multiple input slices before ending a packet: 20 | //! 21 | //! ``` 22 | //! use serial_line_ip::Encoder; 23 | //! 24 | //! const INPUT_1: &[u8] = &[0x01, 0x02, 0x03]; 25 | //! const INPUT_2: &[u8] = &[0x04, 0x05, 0x06]; 26 | //! const EXPECTED: &[u8] = &[0xc0, 27 | //! 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 28 | //! 0xc0 29 | //! ]; 30 | //! let mut output: [u8; 32] = [0; 32]; 31 | //! 32 | //! let mut slip = Encoder::new(); 33 | //! 34 | //! let mut totals = slip.encode(INPUT_1, &mut output).unwrap(); 35 | //! let expected_bytes_written = 1 + INPUT_1.len(); 36 | //! assert_eq!(expected_bytes_written, totals.written); 37 | //! 38 | //! totals += slip.encode(INPUT_2, &mut output[totals.written..]).unwrap(); 39 | //! let expected_bytes_written = expected_bytes_written + INPUT_2.len(); 40 | //! assert_eq!(expected_bytes_written, totals.written); 41 | //! 42 | //! totals += slip.finish(&mut output[totals.written..]).unwrap(); 43 | //! assert_eq!(expected_bytes_written + 1, totals.written); 44 | //! assert_eq!(EXPECTED, &output[..totals.written]); 45 | //! ``` 46 | //! 47 | //! ### Decoding 48 | //! 49 | //! Since the length and number of packets in a data stream (byte slice) 50 | //! is unknown upfront, the length of the input bytes processed, output slice 51 | //! and an indication if the end of a packet was reached, is provided by the 52 | //! decoder: 53 | //! 54 | //! ``` 55 | //! use serial_line_ip::Decoder; 56 | //! 57 | //! const SLIP_ENCODED: [u8; 7] = [ 58 | //! 0xc0, 59 | //! 0x01, 0x02, 0x03, 0x04, 0x05, 60 | //! 0xc0 61 | //! ]; 62 | //! const DATA: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05]; 63 | //! 64 | //! let mut output: [u8; 32] = [0; 32]; 65 | //! let mut slip = Decoder::new(); 66 | //! 67 | //! let (input_bytes_processed, output_slice, is_end_of_packet) = 68 | //! slip.decode(&SLIP_ENCODED, &mut output).unwrap(); 69 | //! 70 | //! assert_eq!(SLIP_ENCODED.len(), input_bytes_processed); 71 | //! assert_eq!(&DATA, output_slice); 72 | //! assert_eq!(true, is_end_of_packet); 73 | //! ``` 74 | 75 | #![deny(missing_docs)] 76 | #![deny(warnings)] 77 | #![no_std] 78 | 79 | mod decoder; 80 | mod encoder; 81 | mod error; 82 | 83 | pub use decoder::Decoder; 84 | pub use encoder::{EncodeTotals, Encoder}; 85 | pub use error::{Error, Result}; 86 | 87 | /// Frame end 88 | const END: u8 = 0xC0; 89 | 90 | /// Frame escape 91 | const ESC: u8 = 0xDB; 92 | 93 | /// Transposed frame end 94 | const ESC_END: u8 = 0xDC; 95 | 96 | /// Transposed frame escape 97 | const ESC_ESC: u8 = 0xDD; 98 | -------------------------------------------------------------------------------- /src/decoder.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// SLIP decode context 4 | pub struct Decoder { 5 | header_found: bool, 6 | esc_seq: [u8; 4], 7 | esc_seq_len: usize, 8 | } 9 | 10 | impl Decoder { 11 | /// Create a new context for SLIP decoding 12 | pub fn new() -> Self { 13 | Decoder { 14 | header_found: false, 15 | esc_seq: [0; 4], 16 | esc_seq_len: 0, 17 | } 18 | } 19 | 20 | /// SLIP decode the input slice into the output slice. 21 | /// 22 | /// This returns the number of bytes processed, an output slice and an indication of 23 | /// the end of the packet. 24 | pub fn decode<'a>(&mut self, input: &'a [u8], output: &'a mut [u8]) 25 | -> Result<(usize, &'a [u8], bool)> 26 | { 27 | let input_len = input.len(); 28 | let mut stream = input; 29 | if !self.header_found { 30 | stream = self.decode_header(stream)?; 31 | } 32 | let res = self.decode_stream(stream, output)?; 33 | 34 | Ok((input_len - res.0.len(), res.1, res.2)) 35 | } 36 | 37 | /// Either process the header successfully or return an error 38 | fn decode_header<'a>(&mut self, input: &'a [u8]) -> Result<&'a [u8]> { 39 | if input.len() < 1 { 40 | // TODO: decode partial headers! For now, just error out... 41 | return Err(Error::BadHeaderDecode); 42 | } 43 | 44 | if input[0] != END { 45 | return Err(Error::BadHeaderDecode); 46 | } 47 | self.header_found = true; 48 | 49 | Ok(&input[1..]) 50 | } 51 | 52 | /// Core stream processing 53 | fn decode_stream<'a>(&mut self, input: &'a [u8], output: &'a mut [u8]) 54 | -> Result<(&'a [u8], &'a [u8], bool)> 55 | { 56 | let mut in_byte = 0; 57 | let mut out_byte = 0; 58 | let mut end = false; 59 | 60 | loop { 61 | if in_byte == input.len() || out_byte == output.len() { 62 | break; 63 | } 64 | 65 | if self.esc_seq_len > 0 { 66 | match input[in_byte] { 67 | ESC_END => { 68 | output[out_byte] = END 69 | } 70 | ESC_ESC => { 71 | output[out_byte] = ESC 72 | } 73 | _ => return Err(Error::BadEscapeSequenceDecode), 74 | } 75 | out_byte += 1; 76 | self.esc_sequence_empty(); 77 | } else { 78 | match input[in_byte] { 79 | ESC => { 80 | self.esc_sequence_push(ESC); 81 | } 82 | END => { 83 | in_byte += 1; 84 | end = true; 85 | break; 86 | } 87 | _ => { 88 | output[out_byte] = input[in_byte]; 89 | out_byte += 1; 90 | } 91 | } 92 | } 93 | in_byte += 1; 94 | } 95 | 96 | Ok((&input[in_byte..], &output[..out_byte], end)) 97 | } 98 | 99 | /// Push a byte onto the escape sequence 100 | fn esc_sequence_push(&mut self, byte: u8) { 101 | self.esc_seq[self.esc_seq_len] = byte; 102 | self.esc_seq_len += 1; 103 | } 104 | 105 | /// Reset the escape sequence 106 | fn esc_sequence_empty(&mut self) { 107 | self.esc_seq_len = 0; 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | 115 | #[test] 116 | fn empty_decode() { 117 | const INPUT: [u8; 2] = [0xc0, 0xc0]; 118 | let mut output: [u8; 32] = [0; 32]; 119 | 120 | let mut slip = Decoder::new(); 121 | let res = slip.decode(&INPUT, &mut output).unwrap(); 122 | assert_eq!(INPUT.len(), res.0); 123 | assert_eq!(&[0;0], res.1); 124 | assert_eq!(true, res.2); 125 | } 126 | 127 | #[test] 128 | fn simple_decode() { 129 | const INPUT: [u8; 7] = [0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0xc0]; 130 | const DATA: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05]; 131 | let mut output: [u8; 32] = [0; 32]; 132 | 133 | let mut slip = Decoder::new(); 134 | let res = slip.decode(&INPUT, &mut output).unwrap(); 135 | assert_eq!(INPUT.len(), res.0); 136 | assert_eq!(&DATA, res.1); 137 | assert_eq!(true, res.2); 138 | } 139 | 140 | /// Ensure that [ESC, ESC_END] -> [END] 141 | #[test] 142 | fn decode_esc_then_esc_end_sequence() { 143 | const INPUT: [u8; 6] = [0xc0, 0x01, 0xdb, 0xdc, 0x03, 0xc0]; 144 | const DATA: [u8; 3] = [0x01, 0xc0, 0x03]; 145 | let mut output: [u8; 200] = [0; 200]; 146 | 147 | let mut slip = Decoder::new(); 148 | let res = slip.decode(&INPUT, &mut output).unwrap(); 149 | assert_eq!(INPUT.len(), res.0); 150 | assert_eq!(&DATA, res.1); 151 | assert_eq!(true, res.2); 152 | } 153 | 154 | /// Ensure that [ESC, ESC_ESC] -> [ESC] 155 | #[test] 156 | fn decode_esc_then_esc_esc_sequence() { 157 | const INPUT: [u8; 6] = [0xc0, 0x01, 0xdb, 0xdd, 0x03, 0xc0]; 158 | const DATA: [u8; 3] = [0x01, 0xdb, 0x03]; 159 | let mut output: [u8; 200] = [0; 200]; 160 | 161 | let mut slip = Decoder::new(); 162 | let res = slip.decode(&INPUT, &mut output).unwrap(); 163 | assert_eq!(INPUT.len(), res.0); 164 | assert_eq!(&DATA, res.1); 165 | assert_eq!(true, res.2); 166 | } 167 | 168 | #[test] 169 | fn multi_part_decode() { 170 | const INPUT_1: [u8; 6] = [0xc0, 0x01, 0x02, 0x03, 0x04, 0x05]; 171 | const INPUT_2: [u8; 6] = [0x05, 0x06, 0x07, 0x08, 0x09, 0xc0]; 172 | const DATA_1: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05]; 173 | const DATA_2: [u8; 5] = [0x05, 0x06, 0x07, 0x08, 0x09]; 174 | let mut output: [u8; 200] = [0; 200]; 175 | 176 | let mut slip = Decoder::new(); 177 | let mut offset = 0; 178 | { 179 | let res = slip.decode(&INPUT_1, &mut output[offset..]).unwrap(); 180 | assert_eq!(INPUT_1.len(), res.0); 181 | assert_eq!(&DATA_1, res.1); 182 | assert_eq!(false, res.2); 183 | offset += res.1.len(); 184 | } 185 | { 186 | let res = slip.decode(&INPUT_2, &mut output[offset..]).unwrap(); 187 | assert_eq!(INPUT_2.len(), res.0); 188 | assert_eq!(&DATA_2, res.1); 189 | assert_eq!(true, res.2); 190 | offset += res.1.len(); 191 | } 192 | assert_eq!(10, offset); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/encoder.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// SLIP encoder context 4 | #[derive(Clone)] 5 | pub struct Encoder { 6 | /// Just keep track of whether we have encoded the header yet 7 | header_written: bool, 8 | } 9 | 10 | /// The return type of `encode` that holds the bytes read and byte written after 11 | /// the encode operation. 12 | pub struct EncodeTotals { 13 | /// Number of bytes that were read from the input buffer. 14 | pub read: usize, 15 | /// Number of bytes that were written to the output buffer. 16 | pub written: usize, 17 | } 18 | 19 | impl Encoder { 20 | /// Create a new context for SLIP encoding 21 | pub fn new() -> Self { 22 | Encoder { 23 | header_written: false, 24 | } 25 | } 26 | 27 | /// Encode a buffer into a SLIP stream and returns the number of input bytes read 28 | /// and output bytes written. 29 | pub fn encode(&mut self, input: &[u8], output: &mut [u8]) -> Result { 30 | let mut out_byte = 0; 31 | if !self.header_written { 32 | if output.len() < 1 { 33 | return Err(Error::NoOutputSpaceForHeader); 34 | } 35 | 36 | output[out_byte] = END; 37 | out_byte = 1; 38 | self.header_written = true; 39 | } 40 | 41 | let mut in_byte = 0; 42 | while in_byte < input.len() { 43 | match input[in_byte] { 44 | ESC => { 45 | if (output.len() - out_byte) < 2 { 46 | break; 47 | } 48 | output[out_byte] = ESC; 49 | output[out_byte + 1] = ESC_ESC; 50 | out_byte += 2; 51 | } 52 | END => { 53 | if (output.len() - out_byte) < 2 { 54 | break; 55 | } 56 | output[out_byte] = ESC; 57 | output[out_byte + 1] = ESC_END; 58 | out_byte += 2; 59 | } 60 | _ => { 61 | if (output.len() - out_byte) < 1 { 62 | break; 63 | } 64 | output[out_byte] = input[in_byte]; 65 | out_byte += 1; 66 | } 67 | } 68 | in_byte += 1; 69 | } 70 | 71 | Ok(EncodeTotals { 72 | read: in_byte, 73 | written: out_byte, 74 | }) 75 | } 76 | 77 | /// Finish encoding the current packet and return the number of output bytes written. 78 | pub fn finish(self, output: &mut [u8]) -> Result { 79 | if output.len() < 1 { 80 | return Err(Error::NoOutputSpaceForEndByte); 81 | } 82 | output[0] = END; 83 | 84 | Ok(EncodeTotals { 85 | read: 0, 86 | written: 1, 87 | }) 88 | } 89 | } 90 | 91 | impl core::ops::AddAssign for EncodeTotals { 92 | fn add_assign(&mut self, other: EncodeTotals) { 93 | *self = EncodeTotals { 94 | read: self.read + other.read, 95 | written: self.written + other.written, 96 | }; 97 | } 98 | } 99 | 100 | #[cfg(test)] 101 | mod tests { 102 | use super::*; 103 | 104 | #[test] 105 | fn empty_encode() { 106 | const EXPECTED: [u8; 2] = [0xc0, 0xc0]; 107 | let mut output: [u8; 32] = [0; 32]; 108 | 109 | let mut slip = Encoder::new(); 110 | let mut totals = slip.encode(&[0; 0], &mut output).unwrap(); 111 | assert_eq!(0, totals.read); 112 | assert_eq!(1, totals.written); 113 | totals += slip.finish(&mut output[totals.written..]).unwrap(); 114 | assert_eq!(0, totals.read); 115 | assert_eq!(2, totals.written); 116 | assert_eq!(&EXPECTED, &output[..totals.written]); 117 | } 118 | 119 | #[test] 120 | fn encode_esc_esc_sequence() { 121 | const INPUT: [u8; 3] = [0x01, ESC, 0x03]; 122 | const EXPECTED: [u8; 6] = [0xc0, 0x01, ESC, ESC_ESC, 0x03, 0xc0]; 123 | let mut output: [u8; 32] = [0; 32]; 124 | 125 | let mut slip = Encoder::new(); 126 | let mut totals = slip.encode(&INPUT, &mut output).unwrap(); 127 | assert_eq!(2 + INPUT.len(), totals.written); 128 | totals += slip.finish(&mut output[totals.written..]).unwrap(); 129 | assert_eq!(INPUT.len(), totals.read); 130 | assert_eq!(3 + INPUT.len(), totals.written); 131 | assert_eq!(&EXPECTED, &output[..totals.written]); 132 | } 133 | #[test] 134 | fn encode_end_esc_sequence() { 135 | const INPUT: [u8; 3] = [0x01, END, 0x03]; 136 | const EXPECTED: [u8; 6] = [0xc0, 0x01, ESC, ESC_END, 0x03, 0xc0]; 137 | let mut output: [u8; 32] = [0; 32]; 138 | 139 | let mut slip = Encoder::new(); 140 | let mut totals = slip.encode(&INPUT, &mut output).unwrap(); 141 | assert_eq!(2 + INPUT.len(), totals.written); 142 | totals += slip.finish(&mut output[totals.written..]).unwrap(); 143 | assert_eq!(INPUT.len(), totals.read); 144 | assert_eq!(3 + INPUT.len(), totals.written); 145 | assert_eq!(&EXPECTED, &output[..totals.written]); 146 | } 147 | 148 | #[test] 149 | fn multi_part_encode() { 150 | const INPUT_1: [u8; 4] = [0x01, 0x02, 0x03, ESC]; 151 | const INPUT_2: [u8; 4] = [0x05, END, 0x07, 0x08]; 152 | const INPUT_3: [u8; 4] = [0x09, 0x0a, ESC, 0x0c]; 153 | const EXPECTED: &[u8] = &[ 154 | 0xc0, 0x01, 0x02, 0x03, ESC, ESC_ESC, 0x05, ESC, ESC_END, 0x07, 0x08, 0x09, 0x0a, ESC, 155 | ESC_ESC, 0x0c, 0xc0, 156 | ]; 157 | let mut output: [u8; 32] = [0; 32]; 158 | 159 | let mut slip = Encoder::new(); 160 | let mut final_totals = EncodeTotals { 161 | read: 0, 162 | written: 0, 163 | }; 164 | 165 | let totals = slip.encode(&INPUT_1, &mut output).unwrap(); 166 | assert_eq!(INPUT_1.len(), totals.read); 167 | assert_eq!(1 + INPUT_1.len() + 1, totals.written); 168 | final_totals += totals; 169 | 170 | let totals = slip 171 | .encode(&INPUT_2, &mut output[final_totals.written..]) 172 | .unwrap(); 173 | assert_eq!(INPUT_2.len(), totals.read); 174 | assert_eq!(INPUT_2.len() + 1, totals.written); 175 | final_totals += totals; 176 | 177 | let totals = slip 178 | .encode(&INPUT_3, &mut output[final_totals.written..]) 179 | .unwrap(); 180 | assert_eq!(INPUT_3.len(), totals.read); 181 | assert_eq!(INPUT_3.len() + 1, totals.written); 182 | final_totals += totals; 183 | 184 | let totals = slip.finish(&mut output[final_totals.written..]).unwrap(); 185 | assert_eq!(0, totals.read); 186 | assert_eq!(1, totals.written); 187 | final_totals += totals; 188 | 189 | assert_eq!(EXPECTED, &output[..final_totals.written]); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------