diff --git a/elfo-network/src/socket/capabilities/compression.rs b/elfo-network/src/socket/capabilities/compression.rs index e33bd68..5eed9d2 100644 --- a/elfo-network/src/socket/capabilities/compression.rs +++ b/elfo-network/src/socket/capabilities/compression.rs @@ -4,67 +4,39 @@ use std::fmt; use crate::config::Preference; +bitflags::bitflags! { + /// Set of algorithms. 24 bits. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub(crate) struct Algorithms: u32 { + const LZ4 = 1; + // NB: Shift by 2: `const ZSTD = 1 << 2;`. + } +} + +/// Compression capabilities. +/// /// Layout: /// ```text -/// Bits -/// 6 2 -/// +---+-----+ -/// | R | Lz4 | -/// +---+-----+ +/// 22 2 +/// ┌────────────┬─────┐ +/// │ Reserved │ Lz4 │ +/// └────────────┴─────┘ /// ``` /// -/// `R` - reserved, any other mean specific compression algorithm. Layout -/// for specific compression algorithm: +/// Each mentioned algorithm here occupies two bits for a reason, here's the +/// layout of those bits: /// ```text -/// Bits -/// 1 1 -/// +---+---+ -/// | S | P | -/// +---+---+ +/// 1 1 +/// ┌───────────┬───────────┐ +/// │ Preferred │ Supported │ +/// └───────────┴───────────┘ /// ``` /// -/// 1. `S` - the compression algorithm is supported. -/// 2. `P` - the compression algorithm is preferred, implies `S`. +/// 1. Preferred - the compression algorithm is preferred, implies `Supported`. +/// 2. Supported - the compression algorithm is supported. #[derive(Debug, Clone, Copy)] pub(crate) struct Compression(u32); -fn write_array( - hide: Option, - algos: Algorithms, - f: &mut fmt::Formatter<'_>, -) -> fmt::Result { - write!(f, "[")?; - let mut need_comma = false; - for (name, _) in algos - .iter_names() - .filter(|(_, algo)| hide.map_or(true, |hide| hide.contains(*algo))) - { - if need_comma { - write!(f, ", ")?; - } - - f.write_str(name)?; - need_comma = true; - } - - write!(f, "]") -} - -impl fmt::Display for Compression { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let preferred = self.preferred(); - let supported = self.supported(); - - write!(f, "(preferred: ")?; - write_array(None, preferred, f)?; - write!(f, ", supported: ")?; - // Don't show preferred in supported, more compact - // output. - write_array(Some(preferred), supported, f)?; - write!(f, ")") - } -} - impl Compression { pub(crate) const fn empty() -> Self { Self::new(Algorithms::empty(), Algorithms::empty()) @@ -75,8 +47,8 @@ impl Compression { } pub(crate) const fn from_bits_truncate(v: u32) -> Self { - let supported = Algorithms::from_bits_truncate(v >> 1); - let preferred = Algorithms::from_bits_truncate(v); + let supported = Algorithms::from_bits_truncate(v); + let preferred = Algorithms::from_bits_truncate(v >> 1); Self::new(supported, preferred) } @@ -90,7 +62,7 @@ impl Compression { // 1 0 1 0 | Supported // ------- // 1 1 1 1 - let joined = (supported << 1) | preferred; + let joined = supported | (preferred << 1); Self(joined) } @@ -138,20 +110,48 @@ impl Compression { pub(crate) const fn supported(self) -> Algorithms { // `preferred` bits would be discarded. - Algorithms::from_bits_truncate(self.0 >> 1) + Algorithms::from_bits_truncate(self.0) } pub(crate) const fn preferred(self) -> Algorithms { // `supported` bits would be discarded. - Algorithms::from_bits_truncate(self.0) + Algorithms::from_bits_truncate(self.0 >> 1) } } -bitflags::bitflags! { - // Actually, 24 bits. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub(crate) struct Algorithms: u32 { - const LZ4 = 1; - // NB: Shift by 2: `const ZSTD = 1 << 2;`. +impl fmt::Display for Compression { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn write_array( + hide: Option, + algos: Algorithms, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(f, "[")?; + let mut need_comma = false; + for (name, _) in algos + .iter_names() + .filter(|(_, algo)| hide.map_or(true, |hide| hide.contains(*algo))) + { + if need_comma { + write!(f, ", ")?; + } + + f.write_str(name)?; + need_comma = true; + } + + write!(f, "]") + } + + let preferred = self.preferred(); + let supported = self.supported(); + + write!(f, "(preferred: ")?; + write_array(None, preferred, f)?; + write!(f, ", supported: ")?; + // Don't show preferred in supported, more compact + // output. + write_array(Some(preferred), supported, f)?; + write!(f, ")") } } diff --git a/elfo-network/src/socket/capabilities/mod.rs b/elfo-network/src/socket/capabilities/mod.rs index 18549c2..cb22f35 100644 --- a/elfo-network/src/socket/capabilities/mod.rs +++ b/elfo-network/src/socket/capabilities/mod.rs @@ -4,18 +4,24 @@ use self::compression::Compression; pub(crate) mod compression; +/// Things supported by the node. +/// +/// ### Layout +/// +/// ```text +/// 24 bits 8 bits +/// ┌─────────────────────┬──────────────────┐ +/// │ Compression │ Reserved │ +/// └─────────────────────┴──────────────────┘ +/// ``` +/// +/// 1. [`Compression`] - compression capabilities. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct Capabilities(u32); -impl fmt::Display for Capabilities { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "(compression: {})", self.compression()) - } -} - impl Capabilities { pub(crate) const fn new(compression: Compression) -> Self { - let compression = compression.bits() as u32; + let compression = compression.bits(); let joined = compression << 8; Self(joined) @@ -41,3 +47,61 @@ impl Capabilities { self.0 } } + +impl fmt::Display for Capabilities { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(compression: {})", self.compression()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use self::compression::Algorithms; + + #[test] + fn capabilities_format_is_compatible_with_020alpha17() { + let caps = Capabilities::new(Compression::new(Algorithms::LZ4, Algorithms::empty())); + let lz4_bit = caps.bits() & (1 << 8); + + assert_eq!(lz4_bit, 1 << 8); + } + + #[test] + fn compression_capabilities_encoded_right_way() { + #[track_caller] + fn case(create: (Algorithms, Algorithms), expect: (Algorithms, Algorithms)) { + let caps = Capabilities::new(Compression::new(create.0, create.1)); + let compr = caps.compression(); + + assert_eq!(compr.supported(), expect.0); + assert_eq!(compr.preferred(), expect.1); + + // Just in case we should decode same caps. + + let bits = caps.bits(); + let same_caps = Capabilities::from_bits_truncate(bits); + + assert_eq!(caps, same_caps); + } + + // Supported does not implies preferred. + case( + (Algorithms::LZ4, Algorithms::empty()), + (Algorithms::LZ4, Algorithms::empty()), + ); + + // Preferred implies supported. + case( + (Algorithms::empty(), Algorithms::LZ4), + (Algorithms::LZ4, Algorithms::LZ4), + ); + + // Nothing ever happens. + case( + (Algorithms::empty(), Algorithms::empty()), + (Algorithms::empty(), Algorithms::empty()), + ); + } +} diff --git a/elfo-network/src/socket/mod.rs b/elfo-network/src/socket/mod.rs index 7392572..93b224b 100644 --- a/elfo-network/src/socket/mod.rs +++ b/elfo-network/src/socket/mod.rs @@ -309,51 +309,6 @@ mod tests { #[derive(PartialEq)] struct TestSocketMessage(String); - #[test] - fn capabilities_format_is_compatible_with_020alpha17() { - let caps = Capabilities::new(Compression::new(Algorithms::LZ4, Algorithms::empty())); - let lz4_bit = caps.bits() & (1 << 8); - - assert_eq!(lz4_bit, 1 << 8); - } - - #[test] - fn compression_capabilities_encoded_right_way() { - #[track_caller] - fn case(create: (Algorithms, Algorithms), expect: (Algorithms, Algorithms)) { - let caps = Capabilities::new(Compression::new(create.0, create.1)); - let compr = caps.compression(); - - assert_eq!(compr.supported(), expect.0); - assert_eq!(compr.preferred(), expect.1); - - // Just in case we should decode same caps. - - let bits = caps.bits(); - let same_caps = Capabilities::from_bits_truncate(bits); - - assert_eq!(caps, same_caps); - } - - // Supported does not implies preferred. - case( - (Algorithms::LZ4, Algorithms::empty()), - (Algorithms::LZ4, Algorithms::empty()), - ); - - // Preferred implies supported. - case( - (Algorithms::empty(), Algorithms::LZ4), - (Algorithms::LZ4, Algorithms::LZ4), - ); - - // Nothing ever happens. - case( - (Algorithms::empty(), Algorithms::empty()), - (Algorithms::empty(), Algorithms::empty()), - ); - } - fn feed_frame(client_socket: &mut Socket, envelope: &NetworkEnvelope) { for _ in 0..100 { client_socket