feat(project): Establish initial IRTT workspace and protocol definitions
This commit is contained in:
commit
cf74887bdf
21 files changed
+1601
No files matched your search
@@ -0,0 +1,235 @@
|
||||
use crate::{varint, ProtoError, Result, PROTOCOL_VERSION};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Params {
|
||||
pub protocol_version: i64,
|
||||
pub duration_ns: i64,
|
||||
pub interval_ns: i64,
|
||||
pub length: i64,
|
||||
pub received_stats: ReceivedStats,
|
||||
pub stamp_at: StampAt,
|
||||
pub clock: Clock,
|
||||
pub dscp: i64,
|
||||
pub server_fill: Option<ServerFill>,
|
||||
}
|
||||
|
||||
impl Params {
|
||||
pub fn with_protocol_defaults() -> Self {
|
||||
Self {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
let mut out = Vec::new();
|
||||
push_int(1, self.protocol_version, &mut out);
|
||||
push_int(2, self.duration_ns, &mut out);
|
||||
push_int(3, self.interval_ns, &mut out);
|
||||
push_int(4, self.length, &mut out);
|
||||
push_int(5, self.received_stats as i64, &mut out);
|
||||
push_int(6, self.stamp_at as i64, &mut out);
|
||||
push_int(7, self.clock as i64, &mut out);
|
||||
push_int(8, self.dscp, &mut out);
|
||||
if let Some(fill) = &self.server_fill {
|
||||
varint::encode_uvarint(9, &mut out);
|
||||
varint::encode_uvarint(fill.value.len() as u64, &mut out);
|
||||
out.extend_from_slice(fill.value.as_bytes());
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn decode(input: &[u8]) -> Result<Self> {
|
||||
let mut params = Self::default();
|
||||
let mut pos = 0;
|
||||
while pos < input.len() {
|
||||
let (tag, used) = varint::decode_uvarint(&input[pos..])?;
|
||||
pos += used;
|
||||
match tag {
|
||||
1 => params.protocol_version = read_int(input, &mut pos)?,
|
||||
2 => params.duration_ns = read_int(input, &mut pos)?,
|
||||
3 => params.interval_ns = read_int(input, &mut pos)?,
|
||||
4 => params.length = read_int(input, &mut pos)?,
|
||||
5 => params.received_stats = ReceivedStats::try_from(read_int(input, &mut pos)?)?,
|
||||
6 => params.stamp_at = StampAt::try_from(read_int(input, &mut pos)?)?,
|
||||
7 => params.clock = Clock::try_from(read_int(input, &mut pos)?)?,
|
||||
8 => params.dscp = read_int(input, &mut pos)?,
|
||||
9 => {
|
||||
let (len, used) = varint::decode_uvarint(&input[pos..])?;
|
||||
pos += used;
|
||||
let len = len as usize;
|
||||
if input.len().saturating_sub(pos) < len {
|
||||
return Err(ProtoError::MalformedParams);
|
||||
}
|
||||
let value = std::str::from_utf8(&input[pos..pos + len])
|
||||
.map_err(|_| ProtoError::InvalidUtf8)?
|
||||
.to_owned();
|
||||
pos += len;
|
||||
params.server_fill = Some(ServerFill { value });
|
||||
}
|
||||
_ => {
|
||||
let (_, used) = varint::decode_uvarint(&input[pos..])?;
|
||||
pos += used;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(params)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ServerFill {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[repr(i64)]
|
||||
pub enum ReceivedStats {
|
||||
#[default]
|
||||
None = 0,
|
||||
Count = 1,
|
||||
Window = 2,
|
||||
Both = 3,
|
||||
}
|
||||
|
||||
impl ReceivedStats {
|
||||
pub fn has_count(self) -> bool {
|
||||
matches!(self, Self::Count | Self::Both)
|
||||
}
|
||||
|
||||
pub fn has_window(self) -> bool {
|
||||
matches!(self, Self::Window | Self::Both)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<i64> for ReceivedStats {
|
||||
type Error = ProtoError;
|
||||
|
||||
fn try_from(value: i64) -> Result<Self> {
|
||||
match value {
|
||||
0 => Ok(Self::None),
|
||||
1 => Ok(Self::Count),
|
||||
2 => Ok(Self::Window),
|
||||
3 => Ok(Self::Both),
|
||||
_ => Err(ProtoError::InvalidEnum {
|
||||
name: "ReceivedStats",
|
||||
value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[repr(i64)]
|
||||
pub enum StampAt {
|
||||
#[default]
|
||||
None = 0,
|
||||
Send = 1,
|
||||
Receive = 2,
|
||||
Both = 3,
|
||||
Midpoint = 4,
|
||||
}
|
||||
|
||||
impl TryFrom<i64> for StampAt {
|
||||
type Error = ProtoError;
|
||||
|
||||
fn try_from(value: i64) -> Result<Self> {
|
||||
match value {
|
||||
0 => Ok(Self::None),
|
||||
1 => Ok(Self::Send),
|
||||
2 => Ok(Self::Receive),
|
||||
3 => Ok(Self::Both),
|
||||
4 => Ok(Self::Midpoint),
|
||||
_ => Err(ProtoError::InvalidEnum {
|
||||
name: "StampAt",
|
||||
value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[repr(i64)]
|
||||
pub enum Clock {
|
||||
#[default]
|
||||
None = 0,
|
||||
Wall = 1,
|
||||
Monotonic = 2,
|
||||
Both = 3,
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
pub fn has_wall(self) -> bool {
|
||||
matches!(self, Self::Wall | Self::Both)
|
||||
}
|
||||
|
||||
pub fn has_mono(self) -> bool {
|
||||
matches!(self, Self::Monotonic | Self::Both)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<i64> for Clock {
|
||||
type Error = ProtoError;
|
||||
|
||||
fn try_from(value: i64) -> Result<Self> {
|
||||
match value {
|
||||
0 => Ok(Self::None),
|
||||
1 => Ok(Self::Wall),
|
||||
2 => Ok(Self::Monotonic),
|
||||
3 => Ok(Self::Both),
|
||||
_ => Err(ProtoError::InvalidEnum {
|
||||
name: "Clock",
|
||||
value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_int(tag: u64, value: i64, out: &mut Vec<u8>) {
|
||||
if value == 0 {
|
||||
return;
|
||||
}
|
||||
varint::encode_uvarint(tag, out);
|
||||
varint::encode_varint(value, out);
|
||||
}
|
||||
|
||||
fn read_int(input: &[u8], pos: &mut usize) -> Result<i64> {
|
||||
let (value, used) = varint::decode_varint(&input[*pos..])?;
|
||||
*pos += used;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn params_round_trip() {
|
||||
let params = Params {
|
||||
protocol_version: 1,
|
||||
duration_ns: 3_000_000_000,
|
||||
interval_ns: 1_000_000_000,
|
||||
length: 1472,
|
||||
received_stats: ReceivedStats::Both,
|
||||
stamp_at: StampAt::Both,
|
||||
clock: Clock::Both,
|
||||
dscp: 184,
|
||||
server_fill: Some(ServerFill {
|
||||
value: "pattern:abc".to_owned(),
|
||||
}),
|
||||
};
|
||||
assert_eq!(Params::decode(¶ms.encode()), Ok(params));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_tags_are_ignored() {
|
||||
let mut encoded = Vec::new();
|
||||
varint::encode_uvarint(99, &mut encoded);
|
||||
varint::encode_varint(123, &mut encoded);
|
||||
varint::encode_uvarint(1, &mut encoded);
|
||||
varint::encode_varint(1, &mut encoded);
|
||||
|
||||
let params = Params::decode(&encoded).unwrap();
|
||||
assert_eq!(params.protocol_version, 1);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user