use opcua_types::{Error, StatusCode};
use tracing::trace;
#[derive(Debug, Clone)]
pub struct SequenceNumberHandle {
is_legacy: bool,
current_value: u32,
}
impl SequenceNumberHandle {
pub fn new(is_legacy: bool) -> Self {
Self {
is_legacy,
current_value: if is_legacy { 1 } else { 0 },
}
}
#[allow(unused)]
pub(crate) fn new_at(is_legacy: bool, value: u32) -> Self {
let max_value = if is_legacy { u32::MAX - 1024 } else { u32::MAX };
Self {
is_legacy,
current_value: value % max_value,
}
}
pub fn max_value(&self) -> u32 {
if self.is_legacy {
u32::MAX - 1024
} else {
u32::MAX
}
}
pub fn is_legacy(&self) -> bool {
self.is_legacy
}
pub(crate) fn set_is_legacy(&mut self, is_legacy: bool) {
self.is_legacy = is_legacy;
if self.current_value > self.max_value() {
self.current_value = self.min_value() + (self.current_value - self.max_value() - 1);
}
}
pub fn min_value(&self) -> u32 {
if self.is_legacy {
1
} else {
0
}
}
pub fn current(&self) -> u32 {
self.current_value
}
pub fn set(&mut self, value: u32) {
self.current_value = value;
}
pub fn increment(&mut self, value: u32) {
let remaining = self.max_value() - self.current_value;
if remaining < value {
self.current_value = self.min_value() + value - remaining - 1;
} else {
self.current_value += value;
}
}
pub fn validate_and_increment(&mut self, incoming_sequence_number: u32) -> Result<(), Error> {
let expected = self.current();
if incoming_sequence_number != expected {
if self.is_legacy() && expected == self.min_value() && incoming_sequence_number < 1024 {
self.set(incoming_sequence_number);
} else {
trace!(
"Expected sequence number {}, got {}",
expected,
incoming_sequence_number
);
return Err(Error::new(
StatusCode::BadSequenceNumberInvalid,
format!(
"Chunk sequence number of {incoming_sequence_number} is not the expected value of {expected}"
),
));
}
}
self.increment(1);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::SequenceNumberHandle;
#[test]
fn test_sequence_numbers() {
let mut seq = SequenceNumberHandle::new(true);
assert_eq!(seq.current(), 1);
assert_eq!(seq.max_value(), u32::MAX - 1024);
assert_eq!(seq.min_value(), 1);
assert!(seq.is_legacy());
seq.increment(1);
assert_eq!(seq.current(), 2);
seq.increment(1022);
assert_eq!(seq.current(), 1024);
seq.increment(u32::MAX - 2048);
assert_eq!(seq.current(), u32::MAX - 1024);
seq.increment(1);
assert_eq!(seq.current(), 1);
seq.increment(u32::MAX - 1026);
assert_eq!(seq.current(), u32::MAX - 1025);
seq.increment(3);
assert_eq!(seq.current(), 2);
}
#[test]
fn test_sequence_numbers_non_legacy() {
let mut seq = SequenceNumberHandle::new(false);
assert_eq!(seq.current(), 0);
assert_eq!(seq.max_value(), u32::MAX);
assert_eq!(seq.min_value(), 0);
assert!(!seq.is_legacy());
seq.increment(1);
assert_eq!(seq.current(), 1);
seq.increment(u32::MAX - 1);
assert_eq!(seq.current(), u32::MAX);
seq.increment(1);
assert_eq!(seq.current(), 0);
seq.increment(u32::MAX - 1);
assert_eq!(seq.current(), u32::MAX - 1);
seq.increment(3);
assert_eq!(seq.current(), 1);
}
#[test]
fn test_sequence_numbers_validate() {
let mut seq = SequenceNumberHandle::new(true);
assert_eq!(seq.current(), 1);
assert!(seq.validate_and_increment(1).is_ok());
assert_eq!(seq.current(), 2);
assert!(seq.validate_and_increment(2).is_ok());
assert_eq!(seq.current(), 3);
assert!(seq.validate_and_increment(5).is_err());
assert_eq!(seq.current(), 3);
seq.set(1);
assert!(seq.validate_and_increment(50).is_ok());
assert_eq!(seq.current(), 51);
assert!(seq.validate_and_increment(50).is_err());
assert_eq!(seq.current(), 51);
assert!(seq.validate_and_increment(51).is_ok());
assert_eq!(seq.current(), 52);
seq.set(u32::MAX - 1024);
assert!(seq.validate_and_increment(u32::MAX - 1024).is_ok());
assert_eq!(seq.current(), 1);
assert!(seq.validate_and_increment(20).is_ok());
assert_eq!(seq.current(), 21);
}
#[test]
fn test_sequence_numbers_validate_non_legacy() {
let mut seq = SequenceNumberHandle::new(false);
assert_eq!(seq.current(), 0);
assert!(seq.validate_and_increment(0).is_ok());
assert_eq!(seq.current(), 1);
assert!(seq.validate_and_increment(1).is_ok());
assert_eq!(seq.current(), 2);
assert!(seq.validate_and_increment(5).is_err());
assert_eq!(seq.current(), 2);
seq.set(0);
assert!(seq.validate_and_increment(50).is_err());
assert_eq!(seq.current(), 0);
assert!(seq.validate_and_increment(0).is_ok());
assert_eq!(seq.current(), 1);
seq.set(u32::MAX);
assert!(seq.validate_and_increment(u32::MAX).is_ok());
assert_eq!(seq.current(), 0);
assert!(seq.validate_and_increment(0).is_ok());
assert_eq!(seq.current(), 1);
}
}