use crate::varint::{self, VarInt};
use super::{
DataPayload, Frame, FrameType, GoawayPayload, HeadersPayload, SettingsPayload, UnknownFrame,
};
pub fn encode_frame_header(
buf: &mut [u8],
frame_type: VarInt,
payload_len: VarInt,
) -> Option<usize> {
let total = frame_type.encoded_len() + payload_len.encoded_len();
if buf.len() < total {
return None;
}
let mut offset = 0;
offset += varint::encode(&mut buf[offset..], frame_type).ok()?;
offset += varint::encode(&mut buf[offset..], payload_len).ok()?;
Some(offset)
}
pub fn encoded_frame_len(frame: &Frame) -> Option<usize> {
let (frame_type, payload_len): (VarInt, VarInt) = match frame {
Frame::Data(p) => (
VarInt::from_static(FrameType::Data as u64),
VarInt::new(p.len() as u64).ok()?,
),
Frame::Headers(p) => (
VarInt::from_static(FrameType::Headers as u64),
VarInt::new(p.len() as u64).ok()?,
),
Frame::Settings(p) => (
VarInt::from_static(FrameType::Settings as u64),
encoded_settings_payload_len(p)?,
),
Frame::Goaway(p) => (
VarInt::from_static(FrameType::Goaway as u64),
VarInt::from_static(p.id().encoded_len() as u64),
),
Frame::MaxPushId(id) => (
VarInt::from_static(FrameType::MaxPushId as u64),
VarInt::from_static(id.encoded_len() as u64),
),
Frame::Unknown(p) => (p.frame_type(), VarInt::new(p.payload().len() as u64).ok()?),
};
let header_len = frame_type.encoded_len() + payload_len.encoded_len();
let payload_size = usize::try_from(payload_len.get()).ok()?;
header_len.checked_add(payload_size)
}
fn encoded_settings_payload_len(payload: &SettingsPayload) -> Option<VarInt> {
let mut total: usize = 0;
for setting in payload.settings() {
let (id, value) = setting.as_wire();
total = total.checked_add(id.encoded_len())?;
total = total.checked_add(value.encoded_len())?;
}
VarInt::new(total as u64).ok()
}
pub fn encode_frame(buf: &mut [u8], frame: &Frame) -> Option<usize> {
let required = encoded_frame_len(frame)?;
if buf.len() < required {
return None;
}
match frame {
Frame::Data(p) => encode_data_frame(buf, p),
Frame::Headers(p) => encode_headers_frame(buf, p),
Frame::Settings(p) => encode_settings_frame(buf, p),
Frame::Goaway(p) => encode_goaway_frame(buf, p),
Frame::MaxPushId(value) => encode_max_push_id_frame(buf, *value),
Frame::Unknown(p) => encode_unknown_frame(buf, p),
}
}
fn encode_data_frame(buf: &mut [u8], payload: &DataPayload) -> Option<usize> {
let frame_type = VarInt::from_static(FrameType::Data as u64);
let payload_len = VarInt::new(payload.len() as u64).ok()?;
let mut offset = encode_frame_header(buf, frame_type, payload_len)?;
buf[offset..offset + payload.len()].copy_from_slice(payload.data());
offset += payload.len();
Some(offset)
}
fn encode_headers_frame(buf: &mut [u8], payload: &HeadersPayload) -> Option<usize> {
let frame_type = VarInt::from_static(FrameType::Headers as u64);
let payload_len = VarInt::new(payload.len() as u64).ok()?;
let mut offset = encode_frame_header(buf, frame_type, payload_len)?;
buf[offset..offset + payload.len()].copy_from_slice(payload.encoded_field_section());
offset += payload.len();
Some(offset)
}
fn encode_settings_frame(buf: &mut [u8], payload: &SettingsPayload) -> Option<usize> {
let frame_type = VarInt::from_static(FrameType::Settings as u64);
let payload_len = encoded_settings_payload_len(payload)?;
let mut offset = encode_frame_header(buf, frame_type, payload_len)?;
for setting in payload.settings() {
let (id, value) = setting.as_wire();
offset += varint::encode(&mut buf[offset..], id).ok()?;
offset += varint::encode(&mut buf[offset..], value).ok()?;
}
Some(offset)
}
fn encode_goaway_frame(buf: &mut [u8], payload: &GoawayPayload) -> Option<usize> {
let frame_type = VarInt::from_static(FrameType::Goaway as u64);
let id = payload.id();
let payload_len = VarInt::from_static(id.encoded_len() as u64);
let mut offset = encode_frame_header(buf, frame_type, payload_len)?;
offset += varint::encode(&mut buf[offset..], id).ok()?;
Some(offset)
}
fn encode_max_push_id_frame(buf: &mut [u8], id: VarInt) -> Option<usize> {
let frame_type = VarInt::from_static(FrameType::MaxPushId as u64);
let payload_len = VarInt::from_static(id.encoded_len() as u64);
let mut offset = encode_frame_header(buf, frame_type, payload_len)?;
offset += varint::encode(&mut buf[offset..], id).ok()?;
Some(offset)
}
fn encode_unknown_frame(buf: &mut [u8], payload: &UnknownFrame) -> Option<usize> {
let frame_type = payload.frame_type();
let bytes = payload.payload();
let payload_len = VarInt::new(bytes.len() as u64).ok()?;
let mut offset = encode_frame_header(buf, frame_type, payload_len)?;
buf[offset..offset + bytes.len()].copy_from_slice(bytes);
offset += bytes.len();
Some(offset)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_frame_header() {
let mut buf = [0u8; 16];
let len =
encode_frame_header(&mut buf, VarInt::from_static(0), VarInt::from_static(0)).unwrap();
assert_eq!(len, 2);
assert_eq!(&buf[..2], &[0x00, 0x00]);
let len =
encode_frame_header(&mut buf, VarInt::from_static(4), VarInt::from_static(10)).unwrap();
assert_eq!(len, 2);
assert_eq!(&buf[..2], &[0x04, 0x0a]);
}
#[test]
fn test_encode_frame_header_buffer_too_short() {
let mut buf = [0u8; 1];
assert_eq!(
encode_frame_header(&mut buf, VarInt::from_static(0), VarInt::from_static(0)),
None
);
}
}