From cc8685c32a1c1b3bbc05c3c72b822bfd28852616 Mon Sep 17 00:00:00 2001 From: Kevin Clark Date: Sun, 18 Aug 2024 23:13:50 -0700 Subject: [PATCH] Allow for no-std build (extracts std feature, on by default) --- CHANGELOG.md | 1 + Cargo.toml | 4 ++++ src/builder.rs | 42 +++++++++++++++++++++++------------------ src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++------------- src/reader.rs | 44 +++++++++++++++++++++++++++++++------------ 5 files changed, 99 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb238c..723486a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # ChangeLog ## Unreleased +- Extracted RtpPacketBuilder::build and Errors to 'std' feature, which is on by default. ### Changed - Switched to Rust 2021 edition. diff --git a/Cargo.toml b/Cargo.toml index 26d9038..2769396 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,7 @@ criterion = "0.5" [[bench]] name = "bench" harness = false + +[features] +default = ["std"] +std = [] \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 05a4c31..39fdfc2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -314,6 +314,7 @@ impl<'a> RtpPacketBuilder<'a> { /// Build the RTP packet. /// On success, it returns a buffer containing the target packet. + #[cfg(feature = "std")] pub fn build(&self) -> Result, RtpPacketBuildError> { self.validate_content()?; @@ -324,7 +325,7 @@ impl<'a> RtpPacketBuilder<'a> { Ok(buffer) } - + fn validate_content(&self) -> Result<(), RtpPacketBuildError> { if (self.payload_type & (!0x7F)) != 0 { return Err(RtpPacketBuildError::PayloadTypeInvalid); @@ -344,10 +345,12 @@ impl<'a> RtpPacketBuilder<'a> { } } +// When Rust 1.81 drops with Error in core, this feature gate can be removed. +#[cfg(feature = "std")] impl std::error::Error for RtpPacketBuildError {} -impl std::fmt::Display for RtpPacketBuildError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for RtpPacketBuildError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "{}", @@ -367,16 +370,17 @@ mod test { #[test] fn test_padded() { - let payload = vec![1u8]; - let packet = RtpPacketBuilder::new() + let payload = [1u8]; + let mut buffer = [0u8; 16]; + RtpPacketBuilder::new() .payload_type(1) .payload(&payload) .padded(Pad::round_to(4)) - .build() + .build_into(&mut buffer) .unwrap(); - assert_eq!(packet.len() & 0x03, 0); - assert!(crate::reader::RtpReader::new(&packet) + assert_eq!(buffer[0] & 0x03, 0); + assert!(crate::reader::RtpReader::new(&buffer) .unwrap() .padding() .is_some()); @@ -384,17 +388,18 @@ mod test { #[test] fn test_padding_not_needed() { - let payload = vec![1u8; 4]; - let packet = RtpPacketBuilder::new() + let mut buffer = [0u8; 16]; + let payload = [1u8; 4]; + let packet_len = RtpPacketBuilder::new() .payload_type(1) .payload(&payload) .padded(Pad::round_to(4)) - .build() + .build_into(&mut buffer) .unwrap(); // assert the length is not increased beyond the 12 bytes of header + the payload - assert_eq!(packet.len(), 12 + payload.len()); - assert!(crate::reader::RtpReader::new(&packet) + assert_eq!(packet_len, 12 + payload.len()); + assert!(crate::reader::RtpReader::new(&buffer[..packet_len]) .unwrap() .padding() .is_none()); @@ -402,19 +407,20 @@ mod test { #[test] fn test_not_padded() { - let payload = vec![1u8]; - let packet = RtpPacketBuilder::new() + let mut buffer = [0u8; 16]; + let payload = [1u8]; + let packet_len = RtpPacketBuilder::new() .payload_type(1) .payload(&payload) - .build() + .build_into(&mut buffer) .unwrap(); - assert_eq!(packet.len() & 0x03, 1); + assert_eq!(packet_len & 0x03, 1); } #[test] fn test_would_run() { - let extension = vec![1u8, 2, 3, 4]; + let extension = [1u8, 2, 3, 4]; let builder = RtpPacketBuilder::new() .payload_type(12) .extension(1, &extension); diff --git a/src/lib.rs b/src/lib.rs index 718524a..61e8650 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,8 @@ //! ``` //! use rtp_rs::*; //! -//! let payload = vec![0u8, 2, 5, 4, 6]; +//! let mut buffer = [0u8; 1024]; +//! let payload = [0u8, 2, 5, 4, 6]; //! let result = RtpPacketBuilder::new() //! .payload_type(111) //! .ssrc(1337) @@ -28,15 +29,39 @@ //! .padded(Pad::round_to(4)) //! .marked(true) //! .payload(&payload) -//! .build(); -//! if let Ok(packet) = result { -//! println!("Packet: {:?}", packet); +//! .build_into(&mut buffer); +//! +//! if let Ok(len) = result { +//! println!("Packet: {:?}", &buffer[..len]); //! } //! ``` +#![cfg_attr(feature = "std", doc = r#" + +Building a packet with std enabled lets you skip the buffer; + +``` +use rtp_rs::*; + +let payload = vec![0u8, 2, 5, 4, 6]; +let result = RtpPacketBuilder::new() + .payload_type(111) + .ssrc(1337) + .sequence(Seq::from(1234)) + .timestamp(666657) + .padded(Pad::round_to(4)) + .marked(true) + .payload(&payload) + .build(); +if let Ok(packet) = result { + println!("Packet: {:?}", packet); +} +```"#)] #![forbid(unsafe_code)] #![deny(rust_2018_idioms, future_incompatible, missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + /// 16 bit RTP sequence number value, as obtained from the `sequence_number()` method of RtpReader. /// /// ``` @@ -104,32 +129,32 @@ impl From for Seq { /// `1` (rather than `-65535`). /// /// This is for symmetry with addition, where for example `Seq(0xffff) + 1` gives `Seq(0x0000)` -impl std::ops::Sub for Seq { +impl core::ops::Sub for Seq { type Output = i32; fn sub(self, rhs: Seq) -> Self::Output { let delta = i32::from(self.0) - i32::from(rhs.0); - if delta < std::i16::MIN as i32 { - std::u16::MAX as i32 + 1 + delta - } else if delta > std::i16::MAX as i32 { - delta - std::u16::MAX as i32 - 1 + if delta < core::i16::MIN as i32 { + core::u16::MAX as i32 + 1 + delta + } else if delta > core::i16::MAX as i32 { + delta - core::u16::MAX as i32 - 1 } else { delta } } } impl PartialOrd for Seq { - fn partial_cmp(&self, other: &Seq) -> Option { + fn partial_cmp(&self, other: &Seq) -> Option { Some(self.cmp(other)) } } impl Ord for Seq { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { (*self - *other).cmp(&0) } } -impl std::ops::Add for Seq { +impl core::ops::Add for Seq { type Output = Seq; fn add(self, rhs: u16) -> Self::Output { @@ -142,7 +167,7 @@ pub trait IntoSeqIterator { /// Produce an `Iterator` over sequence number values fn seq_iter(self) -> SeqIter; } -impl IntoSeqIterator for std::ops::Range { +impl IntoSeqIterator for core::ops::Range { fn seq_iter(self) -> SeqIter { SeqIter(self.start, self.end) } diff --git a/src/reader.rs b/src/reader.rs index ea75097..326936a 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,5 +1,5 @@ use crate::{RtpPacketBuilder, Seq}; -use std::fmt; +use core::fmt; /// Wrapper around a byte-slice of RTP data, providing accessor methods for the RTP header fields. pub struct RtpReader<'a> { @@ -281,10 +281,13 @@ impl<'a> fmt::Debug for RtpReader<'a> { } } +// When Rust 1.81 drops with Error in core, this feature gate can be removed. +#[cfg(feature = "std")] impl std::error::Error for RtpReaderError {} -impl std::fmt::Display for RtpReaderError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for RtpReaderError { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "{}", @@ -301,6 +304,20 @@ impl std::fmt::Display for RtpReaderError { } ) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{}", + match self { + RtpReaderError::BufferTooShort(_) => "buffer too short", + RtpReaderError::UnsupportedVersion(_) => "unsupported version", + RtpReaderError::HeadersTruncated { header_len: _, buffer_len: _ } => "headers truncated", + RtpReaderError::PaddingLengthInvalid(_) => "padding length invalid", + } + ) + } } #[cfg(test)] @@ -368,7 +385,7 @@ mod tests { assert_eq!(1_692_665_255, reader.timestamp()); assert_eq!(0xa242_af01, reader.ssrc()); assert_eq!(379, reader.payload().len()); - format!("{:?}", reader); + format_args!("{:?}", reader); } #[test] @@ -395,30 +412,33 @@ mod tests { #[test] fn builder_juggle() { let reader = RtpReader::new(&TEST_RTP_PACKET).unwrap(); - let buffer = reader.create_builder().build().unwrap(); + let mut buffer = [0u8; 1024]; + let buf_len = reader.create_builder().build_into(&mut buffer).unwrap(); - assert_eq!(buffer.as_slice(), &TEST_RTP_PACKET[..]); + assert_eq!(&buffer[..buf_len], &TEST_RTP_PACKET[..]); } #[test] fn builder_juggle_extension() { let reader = RtpReader::new(&TEST_RTP_PACKET_WITH_EXTENSION).unwrap(); - let buffer = reader.create_builder().build().unwrap(); - assert_eq!(buffer.as_slice(), &TEST_RTP_PACKET_WITH_EXTENSION[..]); + let mut buffer = [0u8; 1024]; + let buf_len = reader.create_builder().build_into(&mut buffer).unwrap(); + assert_eq!(&buffer[..buf_len], &TEST_RTP_PACKET_WITH_EXTENSION[..]); } #[test] fn builder_juggle_clear_payload() { - let new_payload = vec![]; + let new_payload = [0u8; 0]; let reader = RtpReader::new(&TEST_RTP_PACKET_WITH_EXTENSION).unwrap(); - let buffer = reader + let mut buffer = [0u8; 1024]; + let buf_len = reader .create_builder() .payload(&new_payload) - .build() + .build_into(&mut buffer) .unwrap(); let expected = &TEST_RTP_PACKET_WITH_EXTENSION[0..(3 + 4) * 4]; - assert_eq!(buffer.as_slice(), expected); + assert_eq!(&buffer[..buf_len], expected); } #[test]