use super::constants::{
DEFAULT_TTL_G_COUNTER, DEFAULT_TTL_LWW_REGISTER, DEFAULT_TTL_PN_COUNTER, FLAG_HAS_TTL,
TTL_NEVER_EXPIRES, TTL_SUFFIX_SIZE,
};
use super::crdt_type::CrdtType;
pub fn append_ttl(payload: &mut [u8], payload_len: usize, ttl_seconds: u32) -> Option<usize> {
let new_len = payload_len + TTL_SUFFIX_SIZE;
if new_len > payload.len() {
return None;
}
payload[payload_len..new_len].copy_from_slice(&ttl_seconds.to_le_bytes());
Some(new_len)
}
pub fn strip_ttl(flags: u16, payload: &[u8]) -> (&[u8], u32) {
if flags & FLAG_HAS_TTL != 0 && payload.len() >= TTL_SUFFIX_SIZE {
let split = payload.len() - TTL_SUFFIX_SIZE;
let ttl = u32::from_le_bytes([
payload[split],
payload[split + 1],
payload[split + 2],
payload[split + 3],
]);
(&payload[..split], ttl)
} else {
(payload, TTL_NEVER_EXPIRES)
}
}
pub const fn default_ttl_for_crdt(crdt: CrdtType) -> u32 {
match crdt {
CrdtType::LwwRegister => DEFAULT_TTL_LWW_REGISTER,
CrdtType::GCounter => DEFAULT_TTL_G_COUNTER,
CrdtType::PnCounter => DEFAULT_TTL_PN_COUNTER,
CrdtType::OrSet => TTL_NEVER_EXPIRES,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_append_strip() {
let crdt_data = [0x01, 0x02, 0x03];
let mut buf = [0u8; 16];
buf[..3].copy_from_slice(&crdt_data);
let new_len = append_ttl(&mut buf, 3, 300).unwrap();
assert_eq!(new_len, 7);
let (data, ttl) = strip_ttl(FLAG_HAS_TTL, &buf[..new_len]);
assert_eq!(data, &crdt_data);
assert_eq!(ttl, 300);
}
#[test]
fn no_flag_means_no_ttl() {
let payload = [0x01, 0x02, 0x03, 0x04];
let (data, ttl) = strip_ttl(0, &payload);
assert_eq!(data, &payload);
assert_eq!(ttl, TTL_NEVER_EXPIRES);
}
#[test]
fn buffer_overflow_returns_none() {
let mut buf = [0u8; 3];
buf[..2].copy_from_slice(&[0x01, 0x02]);
assert!(append_ttl(&mut buf, 2, 300).is_none());
}
#[test]
fn default_ttl_per_crdt() {
assert_eq!(default_ttl_for_crdt(CrdtType::LwwRegister), 300);
assert_eq!(default_ttl_for_crdt(CrdtType::GCounter), 3600);
assert_eq!(default_ttl_for_crdt(CrdtType::PnCounter), 3600);
assert_eq!(default_ttl_for_crdt(CrdtType::OrSet), TTL_NEVER_EXPIRES);
}
#[test]
fn strip_ttl_short_payload_with_flag() {
let payload = [0x01, 0x02];
let (data, ttl) = strip_ttl(FLAG_HAS_TTL, &payload);
assert_eq!(data, &payload);
assert_eq!(ttl, TTL_NEVER_EXPIRES);
}
}