use crate::api::Options;
use crate::api::ZERO_CHECKSUM_ALTERNATE_ERROR_DETECTION_METHOD_NONE;
use crate::api::ZeroChecksumAlternateErrorDetectionMethod;
use crate::packet::forward_tsn_chunk;
use crate::packet::idata_chunk;
use crate::packet::parameter::Parameter;
use crate::packet::re_config_chunk;
use crate::packet::supported_extensions_parameter::SupportedExtensionsParameter;
use crate::packet::zero_checksum_acceptable_parameter::ZeroChecksumAcceptableParameter;
use std::collections::HashSet;
#[derive(Debug, Clone, Copy)]
pub struct Capabilities {
pub partial_reliability: bool,
pub message_interleaving: bool,
pub reconfig: bool,
pub zero_checksum_method: ZeroChecksumAlternateErrorDetectionMethod,
pub negotiated_maximum_incoming_streams: u16,
pub negotiated_maximum_outgoing_streams: u16,
}
impl Default for Capabilities {
fn default() -> Self {
Self {
partial_reliability: false,
message_interleaving: false,
reconfig: false,
zero_checksum_method: ZERO_CHECKSUM_ALTERNATE_ERROR_DETECTION_METHOD_NONE,
negotiated_maximum_incoming_streams: 0,
negotiated_maximum_outgoing_streams: 0,
}
}
}
impl Capabilities {
pub fn from_parameters(
nbr_outbound_streams: u16,
nbr_inbound_streams: u16,
parameters: &[Parameter],
) -> Self {
let supported: HashSet<u8> = parameters
.iter()
.find_map(|e| match e {
Parameter::SupportedExtensions(SupportedExtensionsParameter { chunk_types }) => {
Some(chunk_types)
}
_ => None,
})
.unwrap_or(&vec![])
.iter()
.cloned()
.collect();
let partial_reliability =
parameters.iter().any(|e| matches!(e, Parameter::ForwardTsnSupported(_)))
|| supported.contains(&forward_tsn_chunk::CHUNK_TYPE);
let message_interleaving = supported.contains(&idata_chunk::CHUNK_TYPE);
let reconfig = supported.contains(&re_config_chunk::CHUNK_TYPE);
let zero_checksum_method = *parameters
.iter()
.find_map(|e| match e {
Parameter::ZeroChecksumAcceptable(ZeroChecksumAcceptableParameter { method }) => {
Some(method)
}
_ => None,
})
.unwrap_or(&ZERO_CHECKSUM_ALTERNATE_ERROR_DETECTION_METHOD_NONE);
Self {
partial_reliability,
message_interleaving,
reconfig,
zero_checksum_method,
negotiated_maximum_incoming_streams: nbr_outbound_streams,
negotiated_maximum_outgoing_streams: nbr_inbound_streams,
}
}
pub fn negotiate(&self, options: &Options) -> Capabilities {
let partial_reliability = options.enable_partial_reliability && self.partial_reliability;
let message_interleaving = options.enable_message_interleaving && self.message_interleaving;
let zero_checksum_method = if options.zero_checksum_alternate_error_detection_method
== self.zero_checksum_method
{
self.zero_checksum_method
} else {
ZERO_CHECKSUM_ALTERNATE_ERROR_DETECTION_METHOD_NONE
};
Capabilities {
partial_reliability,
message_interleaving,
reconfig: self.reconfig,
zero_checksum_method,
negotiated_maximum_incoming_streams: std::cmp::min(
options.announced_maximum_incoming_streams,
self.negotiated_maximum_incoming_streams,
),
negotiated_maximum_outgoing_streams: std::cmp::min(
options.announced_maximum_outgoing_streams,
self.negotiated_maximum_outgoing_streams,
),
}
}
pub fn zero_checksum_enabled(&self) -> bool {
self.zero_checksum_method != ZERO_CHECKSUM_ALTERNATE_ERROR_DETECTION_METHOD_NONE
}
}