diff --git a/nvx-rs/src/protocol/nvxs/record.rs b/nvx-rs/src/protocol/nvxs/record.rs index 769a9068d..adea7d8ee 100644 --- a/nvx-rs/src/protocol/nvxs/record.rs +++ b/nvx-rs/src/protocol/nvxs/record.rs @@ -2,6 +2,7 @@ use std::fmt; +use crate::protocol::outer::encode_header; use crate::protocol::{ CAPABILITY_LEN, OUTER_ACK, OUTER_CREDIT, OUTER_DATA, OUTER_ERROR, OUTER_GUEST_ATTACH, OUTER_HEADER_LEN, OUTER_HOST_ATTACH, OUTER_MAGIC, OUTER_MAX_PAYLOAD, OUTER_MAX_RECEIVE_WINDOW, @@ -315,15 +316,9 @@ pub fn encode_record( check_payload(record_type, payload)?; // The length check bounds the payload by `OUTER_MAX_PAYLOAD`, so it fits the header's field. let length = payload.len() as u32; + let header = encode_header(record_type.code(), instance_id, epoch, sequence, length); output.reserve(OUTER_HEADER_LEN + payload.len()); - output.extend_from_slice(&OUTER_MAGIC); - output.extend_from_slice(&OUTER_VERSION.to_le_bytes()); - output.push(record_type.code()); - output.push(0); - output.extend_from_slice(instance_id); - output.extend_from_slice(&epoch.to_le_bytes()); - output.extend_from_slice(&sequence.to_le_bytes()); - output.extend_from_slice(&length.to_le_bytes()); + output.extend_from_slice(&header); output.extend_from_slice(payload); Ok(()) } diff --git a/nvx-rs/src/protocol/outer.rs b/nvx-rs/src/protocol/outer.rs index 6f766fe4e..3f8731535 100644 --- a/nvx-rs/src/protocol/outer.rs +++ b/nvx-rs/src/protocol/outer.rs @@ -82,19 +82,36 @@ pub fn encode_outer( .ok() .filter(|&length| length as usize <= OUTER_MAX_PAYLOAD) .ok_or_else(|| violation("control payload exceeds the outer protocol limit"))?; + let header = encode_header(record_type, instance_id, epoch, sequence, length); let mut record = Vec::with_capacity(OUTER_HEADER_LEN + payload.len()); - record.extend_from_slice(&OUTER_MAGIC); - record.extend_from_slice(&OUTER_VERSION.to_le_bytes()); - record.push(record_type); - record.push(0); - record.extend_from_slice(instance_id); - record.extend_from_slice(&epoch.to_le_bytes()); - record.extend_from_slice(&sequence.to_le_bytes()); - record.extend_from_slice(&length.to_le_bytes()); + record.extend_from_slice(&header); record.extend_from_slice(payload); Ok(record) } +/// Returns the header of a record: [`OUTER_MAGIC`], [`OUTER_VERSION`], `record_type`, a zero +/// flags byte, `instance_id`, `epoch`, `sequence`, and the payload's `length`, with the integers +/// little-endian. It checks nothing: [`encode_outer`] and +/// [`encode_record`](super::nvxs::encode_record) each validate their record first. +pub(super) fn encode_header( + record_type: u8, + instance_id: &[u8; 16], + epoch: u64, + sequence: u64, + length: u32, +) -> [u8; OUTER_HEADER_LEN] { + let mut header = [0; OUTER_HEADER_LEN]; + header[..4].copy_from_slice(&OUTER_MAGIC); + header[4..6].copy_from_slice(&OUTER_VERSION.to_le_bytes()); + header[6] = record_type; + // The flags byte, header[7], stays zero. + header[8..24].copy_from_slice(instance_id); + header[24..32].copy_from_slice(&epoch.to_le_bytes()); + header[32..40].copy_from_slice(&sequence.to_le_bytes()); + header[40..].copy_from_slice(&length.to_le_bytes()); + header +} + /// Decodes and validates an outer record header. pub fn decode_outer_header(header: &[u8; OUTER_HEADER_LEN]) -> Result { let version = u16::from_le_bytes([header[4], header[5]]);