ssh_format-0.14.1/.cargo_vcs_info.json0000644000000001360000000000100132640ustar { "git": { "sha1": "85901ce3e522bbe2c3e6969042b0d55188c5ef1d" }, "path_in_vcs": "" }ssh_format-0.14.1/.github/dependabot.yml000064400000000000000000000006061046102023000162460ustar 00000000000000version: 2 updates: - package-ecosystem: "github-actions" # Workflow files stored in the # default location of `.github/workflows` directory: "/" schedule: interval: "daily" - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" - package-ecosystem: "cargo" directory: "/ssh_format_error" schedule: interval: "daily" ssh_format-0.14.1/.github/workflows/rust.yml000064400000000000000000000022771046102023000172010ustar 00000000000000name: Rust env: CARGO_TERM_COLOR: always on: push: paths-ignore: - 'README.md' - 'LICENSE' - '.gitignore' - '.github/workflows/dep-autoupgrade.yml' pull_request: paths-ignore: - 'README.md' - 'LICENSE' - '.gitignore' - '.github/workflows/dep-autoupgrade.yml' jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ github.event.repository.name }}-${{ runner.os }}-cargo-check-v2 - name: Run check run: | cargo clippy --all --all-features cargo fmt --all -- --check build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ github.event.repository.name }}-${{ runner.os }}-cargo-test-v2 - name: Run tests run: cargo test --all-features ssh_format-0.14.1/.gitignore000064400000000000000000000006311046102023000140440ustar 00000000000000# Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk # Added by cargo # # already existing elements were commented out /target #Cargo.lock ssh_format-0.14.1/Cargo.toml0000644000000025060000000000100112650ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "ssh_format" version = "0.14.1" authors = ["Jiahao XU "] description = "Data format used to communicate with openssh mux server." readme = "README.md" keywords = [ "serde", "ssh", "serialization", "parsing", "encoding", ] categories = ["encoding"] license = "MIT" repository = "https://github.com/openssh-rust/ssh_format" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.bytes] version = "1.2.1" optional = true [dependencies.serde] version = "1.0" [dependencies.ssh_format_error] version = "0.1" [dev-dependencies.assert_matches] version = "1.5.0" [dev-dependencies.generator] version = "0.7" [dev-dependencies.itertools] version = "0.10.5" [dev-dependencies.serde] version = "1.0" features = ["derive"] [features] is_human_readable = [] ssh_format-0.14.1/Cargo.toml.orig000064400000000000000000000015461046102023000147510ustar 00000000000000[package] name = "ssh_format" version = "0.14.1" edition = "2018" authors = ["Jiahao XU "] license = "MIT" description = "Data format used to communicate with openssh mux server." repository = "https://github.com/openssh-rust/ssh_format" keywords = ["serde", "ssh", "serialization", "parsing", "encoding"] categories = ["encoding"] [workspace] members = ["ssh_format_error"] [features] is_human_readable = [] [dependencies] serde = "1.0" ssh_format_error = { version = "0.1", path = "ssh_format_error" } bytes = { version = "1.2.1", optional = true } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } assert_matches = "1.5.0" generator = "0.7" itertools = "0.10.5" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] ssh_format-0.14.1/LICENSE000064400000000000000000000020521046102023000130600ustar 00000000000000MIT License Copyright (c) 2021 Jiahao XU Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ssh_format-0.14.1/README.md000064400000000000000000000034211046102023000133330ustar 00000000000000# ssh_format [![Rust](https://github.com/openssh-rust/ssh_format/actions/workflows/rust.yml/badge.svg)](https://github.com/openssh-rust/ssh_format/actions/workflows/rust.yml) [![crate.io downloads](https://img.shields.io/crates/d/ssh_format)](https://crates.io/crates/ssh_format) [![crate.io version](https://img.shields.io/crates/v/ssh_format)](https://crates.io/crates/ssh_format) [![docs](https://docs.rs/ssh_format/badge.svg)](https://docs.rs/ssh_format) Data format used to communicate with openssh mux server. Format details: - All integers are encoded in big endian; - Boolean are encoded as `u32` according to [here][1]; - `char` are encoded as `u32`; - Strings and bytes are encoded as length(`u32`) + content, same as [`sshbuf_put_string`]; - `Option::None` are omitted while `Option::Some(v)` has the same encoding as `v` since openssh mux protocol allows optional parameter at the end of the message; - struct/tuple are encoded as-is, unit struct/tuple are omitted; - sequence are encoded as if it is a tuple according to [here][0], thus it cannot be deserialized; - Variant is encoded as index(`u32`) + content encoded as-is (it is expected to manually implement `Serialize` and `Deserialize` to ensure the `variant_index` is the one you expected); - Serializing/Deserializing map is unsupported; [`sshbuf_put_string`]: https://github.com/openssh/openssh-portable/blob/2dc328023f60212cd29504fc05d849133ae47355/sshbuf-getput-basic.c#L514 [0]: https://github.com/openssh/openssh-portable/blob/19b3d846f06697c85957ab79a63454f57f8e22d6/mux.c#L1906 [1]: https://github.com/openssh/openssh-portable/blob/19b3d846f06697c85957ab79a63454f57f8e22d6/mux.c#L1897 ## Feature - `is_human_readable` enables `Serializer::is_human_readable` and `Deserializer::is_human_readable`. ssh_format-0.14.1/src/de.rs000064400000000000000000000361671046102023000136160ustar 00000000000000use std::{borrow::Cow, convert::TryInto, iter, str}; use serde::de::{self, DeserializeSeed, IntoDeserializer, SeqAccess, VariantAccess, Visitor}; use serde::Deserialize; use crate::{Error, Result}; #[derive(Copy, Clone, Debug)] pub struct Deserializer<'de, It> { slice: &'de [u8], iter: It, } impl<'de, It> Deserializer<'de, It> { pub const fn new(iter: It) -> Self { Self { iter, slice: &[] } } pub fn into_inner(self) -> (&'de [u8], It) { (self.slice, self.iter) } } impl<'de> Deserializer<'de, iter::Empty<&'de [u8]>> { pub const fn from_bytes(slice: &'de [u8]) -> Self { Self { slice, iter: iter::empty(), } } } /// Return a deserialized value and trailing bytes. /// /// # Example /// /// Simple Usage: /// /// ```ignore /// let serialized = to_bytes(value).unwrap(); /// // Ignore the size /// let (new_value, _trailing_bytes) = from_bytes::(&serialized[4..]).unwrap(); /// /// assert_eq!(value, new_value); /// ``` /// /// Replace `T` with type of `value`. /// /// More complicated one (sending over socket): /// /// ```ignore /// let buffer = [0, 0, 0, 4]; /// let (size: u32, _trailing_bytes) = from_bytes(&buffer).unwrap(); /// /// let buffer = [0, 0, 4, 0]; /// let (val: , _trailing_bytes) = from_bytes(&buffer).unwrap(); /// ``` /// /// Replace `T` with your own type. pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result<(T, &'a [u8])> where T: Deserialize<'a>, { let mut deserializer = Deserializer::from_bytes(s); let t = T::deserialize(&mut deserializer)?; Ok((t, deserializer.slice)) } impl<'de, It> Deserializer<'de, It> where It: iter::FusedIterator + Iterator, { /// Extract the loop as a separate function so that `Self::update_slice` /// can be trivally inlined. fn update_slice_inner(&mut self) { self.slice = self.iter.find(|slice| !slice.is_empty()).unwrap_or(&[]); } #[inline] fn update_slice(&mut self) { if self.slice.is_empty() { self.update_slice_inner(); } } fn next_byte(&mut self) -> Result { self.update_slice(); let byte = self.slice.first().copied().ok_or(Error::Eof)?; self.slice = &self.slice[1..]; Ok(byte) } fn fill_buffer(&mut self, mut buffer: &mut [u8]) -> Result<()> { loop { if buffer.is_empty() { break Ok(()); } self.update_slice(); if self.slice.is_empty() { break Err(Error::Eof); } let n = self.slice.len().min(buffer.len()); buffer[..n].copy_from_slice(&self.slice[..n]); self.slice = &self.slice[n..]; buffer = &mut buffer[n..]; } } /// * `SIZE` - must not be 0! fn next_bytes_const(&mut self) -> Result<[u8; SIZE]> { assert_ne!(SIZE, 0); let mut bytes = [0_u8; SIZE]; self.fill_buffer(&mut bytes)?; Ok(bytes) } fn next_u32(&mut self) -> Result { Ok(u32::from_be_bytes(self.next_bytes_const()?)) } fn next_bytes(&mut self, size: usize) -> Result> { self.update_slice(); if self.slice.len() >= size { let slice = &self.slice[..size]; self.slice = &self.slice[size..]; Ok(Cow::Borrowed(slice)) } else { let mut bytes = vec![0_u8; size]; self.fill_buffer(&mut bytes)?; Ok(Cow::Owned(bytes)) } } /// Parse &str and &[u8] fn parse_bytes(&mut self) -> Result> { let len: usize = self.next_u32()?.try_into().map_err(|_| Error::TooLong)?; self.next_bytes(len) } /// Is there any remaining data. pub fn has_remaining_data(&mut self) -> bool { self.update_slice(); !self.slice.is_empty() } } macro_rules! impl_for_deserialize_primitive { ( $name:ident, $visitor_fname:ident, $type:ty ) => { fn $name(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.$visitor_fname(<$type>::from_be_bytes(self.next_bytes_const()?)) } }; } impl<'de, 'a, It> de::Deserializer<'de> for &'a mut Deserializer<'de, It> where It: iter::FusedIterator + Iterator, { type Error = Error; fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { match self.next_u32()? { 1 => visitor.visit_bool(true), 0 => visitor.visit_bool(false), _ => Err(Error::InvalidBoolEncoding), } } fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u8(self.next_byte()?) } fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i8(self.next_byte()? as i8) } impl_for_deserialize_primitive!(deserialize_i16, visit_i16, i16); impl_for_deserialize_primitive!(deserialize_i32, visit_i32, i32); impl_for_deserialize_primitive!(deserialize_i64, visit_i64, i64); impl_for_deserialize_primitive!(deserialize_u16, visit_u16, u16); impl_for_deserialize_primitive!(deserialize_u32, visit_u32, u32); impl_for_deserialize_primitive!(deserialize_u64, visit_u64, u64); impl_for_deserialize_primitive!(deserialize_f32, visit_f32, f32); impl_for_deserialize_primitive!(deserialize_f64, visit_f64, f64); fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { match char::from_u32(self.next_u32()?) { Some(ch) => visitor.visit_char(ch), None => Err(Error::InvalidChar), } } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { match self.parse_bytes()? { Cow::Owned(owned_bytes) => visitor.visit_string(String::from_utf8(owned_bytes)?), Cow::Borrowed(bytes) => visitor.visit_borrowed_str(str::from_utf8(bytes)?), } } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { match self.parse_bytes()? { Cow::Owned(owned_bytes) => visitor.visit_byte_buf(owned_bytes), Cow::Borrowed(bytes) => visitor.visit_borrowed_bytes(bytes), } } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_bytes(visitor) } fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_unit() } fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_unit(visitor) } fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_newtype_struct(self) } fn deserialize_tuple(self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_seq(Access { deserializer: self, len, }) } fn deserialize_tuple_struct( self, _name: &'static str, len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_tuple(len, visitor) } fn deserialize_struct( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_tuple(fields.len(), visitor) } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { impl<'a, 'de, It> serde::de::EnumAccess<'de> for &'a mut Deserializer<'de, It> where It: iter::FusedIterator + Iterator, { type Error = Error; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: de::DeserializeSeed<'de>, { let idx: u32 = self.next_u32()?; let val: Result<_> = seed.deserialize(idx.into_deserializer()); Ok((val?, self)) } } visitor.visit_enum(self) } #[cfg(feature = "is_human_readable")] /// Always return `false` fn is_human_readable(&self) -> bool { false } /// Unsupported fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.next_u32()? as usize; visitor.visit_seq(Access { deserializer: self, len, }) } /// Unsupported fn deserialize_any(self, _visitor: V) -> Result where V: Visitor<'de>, { Err(Error::Unsupported(&"deserialize_any")) } /// Unsupported fn deserialize_option(self, _visitor: V) -> Result where V: Visitor<'de>, { Err(Error::Unsupported(&"deserialize_option")) } /// Unsupported fn deserialize_map(self, _visitor: V) -> Result where V: Visitor<'de>, { Err(Error::Unsupported(&"deserialize_map")) } /// Unsupported fn deserialize_identifier(self, _visitor: V) -> Result where V: Visitor<'de>, { Err(Error::Unsupported(&"deserialize_identifier")) } /// Unsupported fn deserialize_ignored_any(self, _visitor: V) -> Result where V: Visitor<'de>, { Err(Error::Unsupported(&"deserialize_ignored_any")) } } impl<'a, 'de, It> VariantAccess<'de> for &'a mut Deserializer<'de, It> where It: iter::FusedIterator + Iterator, { type Error = Error; fn unit_variant(self) -> Result<()> { Ok(()) } fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, { DeserializeSeed::deserialize(seed, self) } fn tuple_variant(self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { de::Deserializer::deserialize_tuple(self, len, visitor) } fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de>, { de::Deserializer::deserialize_tuple(self, fields.len(), visitor) } } struct Access<'a, 'de, It> { deserializer: &'a mut Deserializer<'de, It>, len: usize, } impl<'a, 'de, It> SeqAccess<'de> for Access<'a, 'de, It> where It: iter::FusedIterator + Iterator, { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result> where T: DeserializeSeed<'de>, { if self.len > 0 { self.len -= 1; let value = seed.deserialize(&mut *self.deserializer)?; Ok(Some(value)) } else { Ok(None) } } fn size_hint(&self) -> Option { Some(self.len) } } /// Test deserialization #[cfg(test)] mod tests { use std::fmt::Debug; use assert_matches::assert_matches; use generator::{done, Gn}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use super::*; use crate::to_bytes; /// Generate subslices, plus stuffing empty slices into the returned /// iterator. fn generate_subslices(mut bytes: &[u8], chunk_size: usize) -> impl Iterator { assert_ne!(chunk_size, 0); Gn::new_scoped(move |mut s| loop { for _ in 0..8 { // Stuffing empty slices s.yield_(&bytes[..0]); } let n = bytes.len().min(chunk_size); s.yield_(&bytes[..n]); bytes = &bytes[n..]; if bytes.is_empty() { done!(); } }) } /// First serialize value, then deserialize it. fn test_roundtrip<'de, T>(value: &T) where T: Debug + Eq + Serialize + Deserialize<'de>, { let serialized = to_bytes(value).unwrap().leak(); // Ignore the size let serialized = &serialized[4..]; // Test from_bytes assert_eq!(from_bytes::(serialized).unwrap().0, *value); // Test cutting it into multiple small vectors for chunk_size in 1..serialized.len() { let mut deserializer = Deserializer::new(generate_subslices(serialized, chunk_size).fuse()); let val = T::deserialize(&mut deserializer).unwrap(); assert_eq!(val, *value); let (slice, mut iter) = deserializer.into_inner(); assert_eq!(slice, &[]); assert_eq!(iter.next(), None); } } #[test] fn test_integer() { test_roundtrip(&0x12_u8); test_roundtrip(&0x1234_u16); test_roundtrip(&0x12345678_u32); test_roundtrip(&0x1234567887654321_u64); } #[test] fn test_boolean() { test_roundtrip(&true); test_roundtrip(&false); } #[test] fn test_str() { let s = "Hello, world!"; let serialized = to_bytes(&s).unwrap(); // Ignore the size let deserialized: &str = from_bytes(&serialized[4..]).unwrap().0; assert_eq!(deserialized, s); } #[test] fn test_seq() { test_roundtrip(&vec![0x00_u8, 0x01_u8, 0x10_u8, 0x78_u8]); test_roundtrip(&vec![0x0010_u16, 0x0100_u16, 0x1034_u16, 0x7812_u16]); } #[test] fn test_tuple() { test_roundtrip(&(0x00_u8, 0x0100_u16, 0x1034_u16, 0x7812_u16)); } #[test] fn test_struct() { #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct S { v1: u8, v2: u16, v3: u16, v4: u16, } test_roundtrip(&S { v1: 0x00, v2: 0x0100, v3: 0x1034, v4: 0x7812, }); } #[test] fn test_struct2() { #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct S<'a> { v1: u8, v2: u16, v3: u16, v4: u16, #[serde(borrow)] v5: Cow<'a, str>, } test_roundtrip(&S { v1: 0x00, v2: 0x0100, v3: 0x1034, v4: 0x7812, v5: Cow::Owned((0..100).join(", ")), }); } /// Test EOF error #[test] fn test_eof_error() { assert_matches!(from_bytes::(&[]), Err(Error::Eof)); let s = "Hello, world!"; let serialized = to_bytes(&s).unwrap(); assert_matches!( from_bytes::(&serialized[0..serialized.len() - 1]), Err(Error::Eof) ); } } ssh_format-0.14.1/src/lib.rs000064400000000000000000000032301046102023000137550ustar 00000000000000//! Data format used to communicate with openssh mux server. //! //! Format details: //! - All integers are encoded in big endian; //! - Boolean are encoded as `u32` according to [here][1]; //! - `char` are encoded as `u32`; //! - Strings and bytes are encoded as length(`u32`) + content, same as [`sshbuf_put_string`]; //! - `Option::None` are omitted while `Option::Some(v)` has the same encoding as `v` since //! openssh mux protocol allows optional parameter at the end of the message; //! - struct/tuple are encoded as-is, unit struct/tuple are omitted; //! - sequence are encoded as if it is a tuple according to [here][0], thus it cannot be //! deserialized; //! - Variant is encoded as index(`u32`) + content encoded as-is (it is expected to //! manually implement `Serialize` and `Deserialize` to ensure the `variant_index` //! is the one you expected); //! - Serializing/Deserializing map is unsupported; //! //! [`sshbuf_put_string`]: https://github.com/openssh/openssh-portable/blob/2dc328023f60212cd29504fc05d849133ae47355/sshbuf-getput-basic.c#L514 //! [0]: https://github.com/openssh/openssh-portable/blob/19b3d846f06697c85957ab79a63454f57f8e22d6/mux.c#L1906 //! [1]: https://github.com/openssh/openssh-portable/blob/19b3d846f06697c85957ab79a63454f57f8e22d6/mux.c#L1897 //! ## Feature //! - `is_human_readable` enables `Serializer::is_human_readable` and //! `Deserializer::is_human_readable`. #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![forbid(unsafe_code)] mod de; mod ser; mod ser_output; pub use de::{from_bytes, Deserializer}; pub use ser::{to_bytes, Serializer}; pub use ser_output::SerOutput; pub use ssh_format_error::{Error, Result}; ssh_format-0.14.1/src/ser.rs000064400000000000000000000305131046102023000140040ustar 00000000000000use serde::{ser, Serialize}; use std::convert::TryInto; use crate::{Error, Result, SerOutput}; fn usize_to_u32(v: usize) -> Result { v.try_into().map_err(|_| Error::TooLong) } #[derive(Clone, Debug)] pub struct Serializer> { pub output: T, len: usize, } impl Default for Serializer { fn default() -> Self { Self::new(Default::default()) } } impl Serializer { pub fn new(output: T) -> Self { Self { output, len: 0 } } pub fn reserve(&mut self, additional: usize) { self.output.reserve(additional); } /// * `len` - length of additional data included in the packet. pub fn create_header(&self, len: u32) -> Result<[u8; 4]> { let len: u32 = usize_to_u32(self.len + len as usize)?; Ok(len.to_be_bytes()) } /// Reset the internal counter. /// This would cause [`Self::create_header`] to return `Ok([0, 0, 0, 0])` /// until you call [`Serialize::serialize`] again. pub fn reset_counter(&mut self) { self.len = 0; } fn extend_from_slice(&mut self, other: &[u8]) { self.output.extend_from_slice(other); self.len += other.len(); } fn push(&mut self, byte: u8) { self.output.push(byte); self.len += 1; } fn serialize_usize(&mut self, v: usize) -> Result<()> { ser::Serializer::serialize_u32(self, usize_to_u32(v)?) } } /// Return a byte array with the first 4 bytes representing the size /// of the rest of the serialized message. /// /// See doc of `from_bytes` for examples. pub fn to_bytes(value: &T) -> Result> where T: Serialize, { let mut buffer = vec![0, 0, 0, 0]; let mut serializer = Serializer::new(&mut buffer); value.serialize(&mut serializer)?; let header = serializer.create_header(0)?; buffer[..4].copy_from_slice(&header); Ok(buffer) } macro_rules! impl_for_serialize_primitive { ( $name:ident, $type:ty ) => { fn $name(self, v: $type) -> Result<()> { self.extend_from_slice(&v.to_be_bytes()); Ok(()) } }; } impl<'a, Container: SerOutput> ser::Serializer for &'a mut Serializer { type Ok = (); type Error = Error; type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; type SerializeMap = Self; type SerializeStruct = Self; type SerializeStructVariant = Self; fn serialize_bool(self, v: bool) -> Result<()> { self.serialize_u32(v as u32) } fn serialize_u8(self, v: u8) -> Result<()> { self.push(v); Ok(()) } fn serialize_i8(self, v: i8) -> Result<()> { self.push(v as u8); Ok(()) } impl_for_serialize_primitive!(serialize_i16, i16); impl_for_serialize_primitive!(serialize_i32, i32); impl_for_serialize_primitive!(serialize_i64, i64); impl_for_serialize_primitive!(serialize_u16, u16); impl_for_serialize_primitive!(serialize_u32, u32); impl_for_serialize_primitive!(serialize_u64, u64); impl_for_serialize_primitive!(serialize_f32, f32); impl_for_serialize_primitive!(serialize_f64, f64); fn serialize_char(self, v: char) -> Result<()> { self.serialize_u32(v as u32) } fn serialize_str(self, v: &str) -> Result<()> { fn is_null_byte(byte: &u8) -> bool { *byte == b'\0' } let bytes = v.as_bytes(); let null_byte_counts = bytes.iter().copied().filter(is_null_byte).count(); let len = bytes.len() - null_byte_counts; // Reserve bytes self.reserve(4 + len); self.serialize_usize(len)?; if null_byte_counts == 0 { self.extend_from_slice(v.as_bytes()); } else { bytes .split(is_null_byte) .filter(|slice| !slice.is_empty()) .for_each(|slice| { self.extend_from_slice(slice); }); } Ok(()) } fn serialize_bytes(self, v: &[u8]) -> Result<()> { self.reserve(4 + v.len()); self.serialize_usize(v.len())?; self.extend_from_slice(v); Ok(()) } fn serialize_none(self) -> Result<()> { Ok(()) } fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } fn serialize_unit(self) -> Result<()> { Ok(()) } fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { self.serialize_unit() } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } fn serialize_seq(self, len: Option) -> Result { if let Some(len) = len { self.reserve(4 + len as usize); self.serialize_usize(len)?; } Ok(self) } fn serialize_tuple(self, _len: usize) -> Result { Ok(self) } fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_tuple(len) } fn serialize_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_tuple(len) } fn serialize_unit_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, ) -> Result<()> { self.serialize_u32(variant_index) } fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, variant: &'static str, value: &T, ) -> Result<()> where T: ?Sized + Serialize, { self.serialize_unit_variant(name, variant_index, variant)?; value.serialize(self) } fn serialize_tuple_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.serialize_unit_variant(name, variant_index, variant)?; self.serialize_tuple(len) } fn serialize_struct_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.serialize_unit_variant(name, variant_index, variant)?; self.serialize_tuple(len) } #[cfg(feature = "is_human_readable")] /// Always return false fn is_human_readable(&self) -> bool { false } /// Unsupported fn serialize_map(self, _len: Option) -> Result { Err(Error::Unsupported(&"serialize_map")) } } macro_rules! impl_serialize_trait { ( $name:ident, $function_name:ident ) => { impl<'a, Container: SerOutput> ser::$name for &'a mut Serializer { type Ok = (); type Error = Error; fn $function_name(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } fn end(self) -> Result<()> { Ok(()) } } }; } impl_serialize_trait!(SerializeSeq, serialize_element); impl_serialize_trait!(SerializeTuple, serialize_element); impl_serialize_trait!(SerializeTupleStruct, serialize_field); impl_serialize_trait!(SerializeTupleVariant, serialize_field); /// Unsupported impl<'a, Container: SerOutput> ser::SerializeMap for &'a mut Serializer { type Ok = (); type Error = Error; /// Unsupported fn serialize_key(&mut self, _key: &T) -> Result<()> where T: ?Sized + Serialize, { Err(Error::Unsupported(&"serialize_map")) } /// Unsupported fn serialize_value(&mut self, _value: &T) -> Result<()> where T: ?Sized + Serialize, { Err(Error::Unsupported(&"serialize_map")) } /// Unsupported fn end(self) -> Result<()> { Err(Error::Unsupported(&"serialize_map")) } } impl<'a, Container: SerOutput> ser::SerializeStruct for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } fn end(self) -> Result<()> { Ok(()) } } impl<'a, Container: SerOutput> ser::SerializeStructVariant for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeStruct::serialize_field(self, key, value) } fn end(self) -> Result<()> { ser::SerializeStruct::end(self) } } #[cfg(test)] mod tests { use crate::{to_bytes, Serializer}; use serde::{ser, Serialize}; use std::convert::TryInto; #[test] fn test_integer() { assert_eq!(to_bytes(&0x12_u8).unwrap(), [0, 0, 0, 1, 0x12]); assert_eq!(to_bytes(&0x1234_u16).unwrap(), [0, 0, 0, 2, 0x12, 0x34]); assert_eq!( to_bytes(&0x12345678_u32).unwrap(), [0, 0, 0, 4, 0x12, 0x34, 0x56, 0x78] ); assert_eq!( to_bytes(&0x1234567887654321_u64).unwrap(), [0, 0, 0, 8, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21] ); } #[test] fn test_boolean() { assert_eq!(to_bytes(&true).unwrap(), [0, 0, 0, 4, 0, 0, 0, 1]); assert_eq!(to_bytes(&false).unwrap(), [0, 0, 0, 4, 0, 0, 0, 0]); } #[test] fn test_str() { let s = "Hello, world!"; let serialized = to_bytes(&s).unwrap(); let len: u32 = (serialized.len() - 4).try_into().unwrap(); assert_eq!(&serialized[..4], len.to_be_bytes()); assert_eq!(&serialized[4..8], (s.len() as u32).to_be_bytes()); assert_eq!(&serialized[8..], s.as_bytes()); } #[test] fn test_str_with_null() { let s = "\0Hello, world!"; let serialized = to_bytes(&s).unwrap(); let len: u32 = (serialized.len() - 4).try_into().unwrap(); assert_eq!(&serialized[..4], len.to_be_bytes()); assert_eq!(&serialized[4..8], ((s.len() - 1) as u32).to_be_bytes()); assert_eq!(&serialized[8..], &s.as_bytes()[1..]); } #[test] fn test_array() { let array = [0x00_u8, 0x01_u8, 0x10_u8, 0x78_u8]; let slice: &[_] = &array; let serialized = to_bytes(&slice).unwrap(); assert_eq!(serialized[..4], [0, 0, 0, 8]); assert_eq!(serialized[4..8], [0, 0, 0, 4]); assert_eq!(serialized[8..], array); let slice: &[_] = &[0x0010_u16, 0x0100_u16, 0x1034_u16, 0x7812_u16]; assert_eq!( to_bytes(&slice).unwrap(), &[0, 0, 0, 12, 0, 0, 0, 4, 0x00, 0x10, 0x01, 0x00, 0x10, 0x34, 0x78, 0x12_u8] ); } #[test] fn test_tuple() { assert_eq!( to_bytes(&(0x00_u8, 0x0100_u16, 0x1034_u16, 0x7812_u16)).unwrap(), &[0, 0, 0, 7, 0x00_u8, 0x01_u8, 0x00_u8, 0x10_u8, 0x34_u8, 0x78_u8, 0x12_u8] ); } #[test] fn test_struct() { #[derive(Serialize)] struct S { v1: u8, v2: u16, v3: u16, v4: u16, } let v = S { v1: 0x00, v2: 0x0100, v3: 0x1034, v4: 0x7812, }; assert_eq!( to_bytes(&v).unwrap(), &[0, 0, 0, 7, 0x00_u8, 0x01_u8, 0x00_u8, 0x10_u8, 0x34_u8, 0x78_u8, 0x12_u8] ); } #[test] fn test_enum() { use ser::Serializer as SerdeSerializerTrait; let mut serializer: Serializer> = Serializer::default(); serializer.serialize_unit_variant("", 1, "").unwrap(); assert_eq!(serializer.create_header(0).unwrap(), [0, 0, 0, 4]); assert_eq!(serializer.output, [0, 0, 0, 1]); // Reset serializer serializer.reset_counter(); serializer.output.clear(); serializer.serialize_newtype_variant("", 0, "", &3).unwrap(); assert_eq!(serializer.create_header(0).unwrap(), [0, 0, 0, 8]); assert_eq!(serializer.output, [0, 0, 0, 0, 0, 0, 0, 3]); } } ssh_format-0.14.1/src/ser_output.rs000064400000000000000000000025441046102023000154270ustar 00000000000000/// A trait for which can be used to store serialized output. pub trait SerOutput { fn extend_from_slice(&mut self, other: &[u8]); fn push(&mut self, byte: u8); /// Reserves capacity for at least additional more bytes to be inserted. /// /// More than additional bytes may be reserved in order to avoid frequent /// reallocations. A call to reserve may result in an allocation. fn reserve(&mut self, additional: usize); } impl SerOutput for &mut T { fn extend_from_slice(&mut self, other: &[u8]) { (*self).extend_from_slice(other) } fn push(&mut self, byte: u8) { (*self).push(byte) } fn reserve(&mut self, additional: usize) { (*self).reserve(additional); } } impl SerOutput for Vec { fn extend_from_slice(&mut self, other: &[u8]) { self.extend_from_slice(other) } fn push(&mut self, byte: u8) { self.push(byte) } fn reserve(&mut self, additional: usize) { self.reserve(additional); } } #[cfg(feature = "bytes")] impl SerOutput for bytes::BytesMut { fn extend_from_slice(&mut self, other: &[u8]) { self.extend_from_slice(other) } fn push(&mut self, byte: u8) { bytes::BufMut::put_u8(self, byte) } fn reserve(&mut self, additional: usize) { self.reserve(additional); } }