use bytes::Buf;
use crate::Timescale;
use crate::coding::{Decode, DecodeError, Encode, EncodeError};
use super::{GroupOrder, Version};
const MAX_PROPERTIES: u64 = 64;
const MAX_KVP_VALUE_LEN: usize = (1 << 16) - 1;
const TIMESCALE: u64 = 0x08;
const DEFAULT_PUBLISHER_GROUP_ORDER: u64 = 0x22;
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub struct Properties {
pub timescale: Option<Timescale>,
pub group_order: Option<GroupOrder>,
}
impl Properties {
pub fn encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
match version {
Version::Draft14 | Version::Draft15 | Version::Draft16 => return Ok(()),
_ => {}
}
let mut prev_type = 0;
if let Some(timescale) = self.timescale {
TIMESCALE.encode(w, version)?;
u64::from(timescale).encode(w, version)?;
prev_type = TIMESCALE;
}
if let Some(group_order) = self.group_order {
(DEFAULT_PUBLISHER_GROUP_ORDER - prev_type).encode(w, version)?;
u64::from(u8::from(group_order)).encode(w, version)?;
}
Ok(())
}
pub fn decode<R: Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
let mut properties = Self::default();
match version {
Version::Draft14 | Version::Draft15 => return Ok(properties),
_ => {}
}
let mut prev_type: u64 = 0;
let mut i: u64 = 0;
while r.has_remaining() {
if i >= MAX_PROPERTIES {
return Err(DecodeError::TooMany);
}
let delta = u64::decode(r, version)?;
let abs = if i == 0 {
delta
} else {
prev_type.checked_add(delta).ok_or(DecodeError::BoundsExceeded)?
};
prev_type = abs;
i += 1;
if abs % 2 == 0 {
let value = u64::decode(r, version)?;
match abs {
TIMESCALE => {
properties.timescale = Timescale::new(value).ok();
}
DEFAULT_PUBLISHER_GROUP_ORDER => {
properties.group_order = match value {
1 => Some(GroupOrder::Ascending),
2 => Some(GroupOrder::Descending),
_ => return Err(DecodeError::InvalidValue),
};
}
_ => {}
}
} else {
let len = u64::decode(r, version)? as usize;
if len > MAX_KVP_VALUE_LEN {
return Err(DecodeError::BoundsExceeded);
}
if r.remaining() < len {
return Err(DecodeError::Short);
}
r.advance(len);
}
}
Ok(properties)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::coding::Encode;
use bytes::BytesMut;
#[test]
fn test_skip_empty_properties() {
let mut buf = bytes::Bytes::new();
assert_eq!(
Properties::decode(&mut buf, Version::Draft17).unwrap(),
Properties::default()
);
}
#[test]
fn test_skip_varint_property() {
let mut buf = BytesMut::new();
0x02u64.encode(&mut buf, Version::Draft17).unwrap(); 5000u64.encode(&mut buf, Version::Draft17).unwrap(); let mut bytes = buf.freeze();
Properties::decode(&mut bytes, Version::Draft17).unwrap();
assert!(!bytes.has_remaining());
}
#[test]
fn test_skip_bytes_property() {
let mut buf = BytesMut::new();
0x0Bu64.encode(&mut buf, Version::Draft17).unwrap(); 3u64.encode(&mut buf, Version::Draft17).unwrap(); buf.extend_from_slice(&[0x01, 0x02, 0x03]); let mut bytes = buf.freeze();
Properties::decode(&mut bytes, Version::Draft17).unwrap();
assert!(!bytes.has_remaining());
}
#[test]
fn test_skip_multiple_properties() {
let mut buf = BytesMut::new();
0x02u64.encode(&mut buf, Version::Draft17).unwrap();
1000u64.encode(&mut buf, Version::Draft17).unwrap();
0x02u64.encode(&mut buf, Version::Draft17).unwrap();
2000u64.encode(&mut buf, Version::Draft17).unwrap();
0x07u64.encode(&mut buf, Version::Draft17).unwrap();
2u64.encode(&mut buf, Version::Draft17).unwrap();
buf.extend_from_slice(&[0xAA, 0xBB]);
let mut bytes = buf.freeze();
Properties::decode(&mut bytes, Version::Draft17).unwrap();
assert!(!bytes.has_remaining());
}
#[test]
fn test_round_trip() {
let properties = Properties {
timescale: Some(Timescale::MICRO),
group_order: Some(GroupOrder::Descending),
};
let mut buf = BytesMut::new();
properties.encode(&mut buf, Version::Draft18).unwrap();
let mut bytes = buf.freeze();
assert_eq!(Properties::decode(&mut bytes, Version::Draft18).unwrap(), properties);
assert!(!bytes.has_remaining());
}
#[test]
fn test_rejects_zero_group_order() {
let mut buf = BytesMut::new();
0x22u64.encode(&mut buf, Version::Draft18).unwrap();
0u64.encode(&mut buf, Version::Draft18).unwrap();
let mut bytes = buf.freeze();
assert!(Properties::decode(&mut bytes, Version::Draft18).is_err());
}
#[test]
fn test_decodes_draft16_track_extensions() {
let mut buf = BytesMut::new();
0x22u64.encode(&mut buf, Version::Draft16).unwrap();
2u64.encode(&mut buf, Version::Draft16).unwrap();
let mut bytes = buf.freeze();
let properties = Properties::decode(&mut bytes, Version::Draft16).unwrap();
assert_eq!(properties.group_order, Some(GroupOrder::Descending));
assert!(!bytes.has_remaining());
}
#[test]
fn test_round_trip_group_order_only() {
let properties = Properties {
timescale: None,
group_order: Some(GroupOrder::Descending),
};
let mut buf = BytesMut::new();
properties.encode(&mut buf, Version::Draft18).unwrap();
let mut bytes = buf.freeze();
assert_eq!(Properties::decode(&mut bytes, Version::Draft18).unwrap(), properties);
assert!(!bytes.has_remaining());
}
}