1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-09 16:59:34 +00:00

remove unnecessary allocations

This commit is contained in:
Luro02 2020-02-06 12:27:48 +01:00
parent aae3809545
commit 1b0eb56224
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
10 changed files with 59 additions and 134 deletions

View file

@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
@ -12,7 +13,7 @@ pub(crate) struct Lines<'a> {
} }
impl<'a> Iterator for Lines<'a> { impl<'a> Iterator for Lines<'a> {
type Item = crate::Result<Line>; type Item = crate::Result<Line<'a>>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let mut stream_inf = false; let mut stream_inf = false;
@ -32,10 +33,7 @@ impl<'a> Iterator for Lines<'a> {
continue; continue;
} else if line.starts_with("#EXT") { } else if line.starts_with("#EXT") {
match line.parse() { return Some(Tag::try_from(line).map(Line::Tag));
Ok(value) => return Some(Ok(Line::Tag(value))),
Err(e) => return Some(Err(e)),
}
} else if line.starts_with('#') { } else if line.starts_with('#') {
continue; // ignore comments continue; // ignore comments
} else { } else {
@ -44,17 +42,15 @@ impl<'a> Iterator for Lines<'a> {
stream_inf = false; stream_inf = false;
if let Some(first_line) = stream_inf_line { if let Some(first_line) = stream_inf_line {
match format!("{}\n{}", first_line, line).parse() { return Some(
Ok(value) => { tags::ExtXStreamInf::from_str(&format!("{}\n{}", first_line, line))
return Some(Ok(Line::Tag(value))); .map(|v| Line::Tag(Tag::ExtXStreamInf(v))),
} );
Err(e) => return Some(Err(e)),
}
} else { } else {
continue; continue;
} }
} else { } else {
return Some(Ok(Line::Uri(line.to_string()))); return Some(Ok(Line::Uri(line)));
} }
} }
} }
@ -73,15 +69,14 @@ impl<'a> From<&'a str> for Lines<'a> {
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub(crate) enum Line { pub(crate) enum Line<'a> {
Tag(Tag), Tag(Tag<'a>),
Uri(String), Uri(&'a str),
} }
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub(crate) enum Tag { pub(crate) enum Tag<'a> {
ExtM3u(tags::ExtM3u),
ExtXVersion(tags::ExtXVersion), ExtXVersion(tags::ExtXVersion),
ExtInf(tags::ExtInf), ExtInf(tags::ExtInf),
ExtXByteRange(tags::ExtXByteRange), ExtXByteRange(tags::ExtXByteRange),
@ -103,13 +98,12 @@ pub(crate) enum Tag {
ExtXSessionKey(tags::ExtXSessionKey), ExtXSessionKey(tags::ExtXSessionKey),
ExtXIndependentSegments(tags::ExtXIndependentSegments), ExtXIndependentSegments(tags::ExtXIndependentSegments),
ExtXStart(tags::ExtXStart), ExtXStart(tags::ExtXStart),
Unknown(String), Unknown(&'a str),
} }
impl fmt::Display for Tag { impl<'a> fmt::Display for Tag<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self { match &self {
Self::ExtM3u(value) => value.fmt(f),
Self::ExtXVersion(value) => value.fmt(f), Self::ExtXVersion(value) => value.fmt(f),
Self::ExtInf(value) => value.fmt(f), Self::ExtInf(value) => value.fmt(f),
Self::ExtXByteRange(value) => value.fmt(f), Self::ExtXByteRange(value) => value.fmt(f),
@ -136,13 +130,11 @@ impl fmt::Display for Tag {
} }
} }
impl FromStr for Tag { impl<'a> TryFrom<&'a str> for Tag<'a> {
type Err = Error; type Error = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn try_from(input: &'a str) -> Result<Self, Self::Error> {
if input.starts_with(tags::ExtM3u::PREFIX) { if input.starts_with(tags::ExtXVersion::PREFIX) {
input.parse().map(Self::ExtM3u)
} else if input.starts_with(tags::ExtXVersion::PREFIX) {
input.parse().map(Self::ExtXVersion) input.parse().map(Self::ExtXVersion)
} else if input.starts_with(tags::ExtInf::PREFIX) { } else if input.starts_with(tags::ExtInf::PREFIX) {
input.parse().map(Self::ExtInf) input.parse().map(Self::ExtInf)
@ -185,7 +177,7 @@ impl FromStr for Tag {
} else if input.starts_with(tags::ExtXStart::PREFIX) { } else if input.starts_with(tags::ExtXStart::PREFIX) {
input.parse().map(Self::ExtXStart) input.parse().map(Self::ExtXStart)
} else { } else {
Ok(Self::Unknown(input.to_string())) Ok(Self::Unknown(input))
} }
} }
} }

View file

@ -11,6 +11,7 @@ use crate::tags::{
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion, ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion,
}; };
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion}; use crate::types::{ClosedCaptions, MediaType, ProtocolVersion};
use crate::utils::tag;
use crate::{Error, RequiredVersion}; use crate::{Error, RequiredVersion};
/// Master playlist. /// Master playlist.
@ -263,6 +264,7 @@ impl FromStr for MasterPlaylist {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, ExtM3u::PREFIX)?;
let mut builder = Self::builder(); let mut builder = Self::builder();
let mut media_tags = vec![]; let mut media_tags = vec![];
@ -272,23 +274,15 @@ impl FromStr for MasterPlaylist {
let mut session_key_tags = vec![]; let mut session_key_tags = vec![];
let mut unknown_tags = vec![]; let mut unknown_tags = vec![];
for (i, line) in Lines::from(input).enumerate() { for line in Lines::from(input) {
match line? { match line? {
Line::Tag(tag) => { Line::Tag(tag) => {
if i == 0 {
if tag != Tag::ExtM3u(ExtM3u) {
return Err(Error::invalid_input());
}
continue;
}
match tag { match tag {
Tag::ExtM3u(_) => {
return Err(Error::invalid_input());
}
Tag::ExtXVersion(_) => { Tag::ExtXVersion(_) => {
// This tag can be ignored, because the // This tag can be ignored, because the
// MasterPlaylist will automatically set the // MasterPlaylist will automatically set the
// ExtXVersion tag to correct version! // ExtXVersion tag to the minimum required version
// TODO: this might be verified?
} }
Tag::ExtInf(_) Tag::ExtInf(_)
| Tag::ExtXByteRange(_) | Tag::ExtXByteRange(_)

View file

@ -12,6 +12,7 @@ use crate::tags::{
ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion, ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion,
}; };
use crate::types::ProtocolVersion; use crate::types::ProtocolVersion;
use crate::utils::tag;
use crate::{Encrypted, Error, RequiredVersion}; use crate::{Encrypted, Error, RequiredVersion};
/// Media playlist. /// Media playlist.
@ -276,6 +277,8 @@ fn parse_media_playlist(
input: &str, input: &str,
builder: &mut MediaPlaylistBuilder, builder: &mut MediaPlaylistBuilder,
) -> crate::Result<MediaPlaylist> { ) -> crate::Result<MediaPlaylist> {
let input = tag(input, "#EXTM3U")?;
let mut segment = MediaSegment::builder(); let mut segment = MediaSegment::builder();
let mut segments = vec![]; let mut segments = vec![];
@ -285,17 +288,10 @@ fn parse_media_playlist(
let mut available_key_tags: Vec<crate::tags::ExtXKey> = vec![]; let mut available_key_tags: Vec<crate::tags::ExtXKey> = vec![];
for (i, line) in Lines::from(input).enumerate() { for line in Lines::from(input) {
match line? { match line? {
Line::Tag(tag) => { Line::Tag(tag) => {
if i == 0 {
if tag != Tag::ExtM3u(ExtM3u) {
return Err(Error::custom("m3u8 doesn't start with #EXTM3U"));
}
continue;
}
match tag { match tag {
Tag::ExtM3u(_) => return Err(Error::invalid_input()),
Tag::ExtInf(t) => { Tag::ExtInf(t) => {
has_partial_segment = true; has_partial_segment = true;
segment.inf_tag(t); segment.inf_tag(t);

View file

@ -11,31 +11,12 @@ use crate::{Error, RequiredVersion};
/// Playlist file. /// Playlist file.
/// It is the at the start of every [`Media Playlist`] and [`Master Playlist`]. /// It is the at the start of every [`Media Playlist`] and [`Master Playlist`].
/// ///
/// # Examples
///
/// Parsing from a [`str`]:
///
/// ```
/// # use hls_m3u8::tags::ExtM3u;
/// #
/// assert_eq!("#EXTM3U".parse::<ExtM3u>()?, ExtM3u);
/// # Ok::<(), Box<dyn ::std::error::Error>>(())
/// ```
///
/// Converting to a [`str`]:
///
/// ```
/// # use hls_m3u8::tags::ExtM3u;
/// #
/// assert_eq!("#EXTM3U".to_string(), ExtM3u.to_string());
/// ```
///
/// [`Media Playlist`]: crate::MediaPlaylist /// [`Media Playlist`]: crate::MediaPlaylist
/// [`Master Playlist`]: crate::MasterPlaylist /// [`Master Playlist`]: crate::MasterPlaylist
/// [`M3U`]: https://en.wikipedia.org/wiki/M3U /// [`M3U`]: https://en.wikipedia.org/wiki/M3U
/// [4.3.1.1. EXTM3U]: https://tools.ietf.org/html/rfc8216#section-4.3.1.1 /// [4.3.1.1. EXTM3U]: https://tools.ietf.org/html/rfc8216#section-4.3.1.1
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct ExtM3u; pub(crate) struct ExtM3u;
impl ExtM3u { impl ExtM3u {
pub(crate) const PREFIX: &'static str = "#EXTM3U"; pub(crate) const PREFIX: &'static str = "#EXTM3U";

View file

@ -1,5 +1,5 @@
mod m3u; mod m3u;
mod version; mod version;
pub use m3u::*; pub(crate) use m3u::*;
pub use version::*; pub use version::*;

View file

@ -66,23 +66,7 @@ impl FromStr for ExtXByteRange {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
let tokens = input.splitn(2, '@').collect::<Vec<_>>(); Ok(Self(ByteRange::from_str(input)?))
if tokens.is_empty() {
return Err(Error::invalid_input());
}
let length = tokens[0].parse()?;
let start = {
if tokens.len() == 2 {
Some(tokens[1].parse()?)
} else {
None
}
};
Ok(Self::new(length, start))
} }
} }

View file

@ -151,29 +151,14 @@ impl FromStr for ExtInf {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?; let mut input = tag(input, Self::PREFIX)?.splitn(2, ',');
let tokens = input.splitn(2, ',').collect::<Vec<_>>();
if tokens.is_empty() { let duration = Duration::from_secs_f64(input.next().unwrap().parse()?);
return Err(Error::custom(format!( let title = input
"failed to parse #EXTINF tag, couldn't split input: {:?}", .next()
input .map(|value| value.trim())
))); .filter(|value| !value.is_empty())
} .map(|value| value.to_string());
let duration = Duration::from_secs_f64(tokens[0].parse()?);
let title = {
if tokens.len() >= 2 {
if tokens[1].trim().is_empty() {
None
} else {
Some(tokens[1].to_string())
}
} else {
None
}
};
Ok(Self { duration, title }) Ok(Self { duration, title })
} }

View file

@ -74,22 +74,15 @@ impl fmt::Display for ByteRange {
impl FromStr for ByteRange { impl FromStr for ByteRange {
type Err = Error; type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let tokens = s.splitn(2, '@').collect::<Vec<_>>(); let mut input = input.splitn(2, '@');
if tokens.is_empty() { let length = input
return Err(Error::invalid_input()); .next()
} .ok_or_else(|| Error::custom("missing length for #EXT-X-BYTERANGE"))
.and_then(|s| s.parse().map_err(Error::parse_int))?;
let length = tokens[0].parse()?; let start = input.next().map(str::parse).transpose()?;
let start = {
if tokens.len() == 2 {
Some(tokens[1].parse()?)
} else {
None
}
};
Ok(Self::new(length, start)) Ok(Self::new(length, start))
} }

View file

@ -83,17 +83,17 @@ impl FromStr for Channels {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let parameters = input.split('/').collect::<Vec<_>>(); let mut parameters = input.split('/');
let channel_number = parameters let channel_number = parameters
.first() .next()
.ok_or_else(|| Error::missing_attribute("first parameter of channels"))? .ok_or_else(|| Error::missing_attribute("first parameter of channels"))?
.parse() .parse()
.map_err(Error::parse_int)?; .map_err(Error::parse_int)?;
Ok(Self { Ok(Self {
channel_number, channel_number,
unknown: parameters[1..].iter().map(|v| v.to_string()).collect(), unknown: parameters.map(|v| (*v).to_string()).collect(),
}) })
} }
} }

View file

@ -36,19 +36,19 @@ impl FromStr for Resolution {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let tokens = input.splitn(2, 'x').collect::<Vec<_>>(); let mut input = input.splitn(2, 'x');
if tokens.len() != 2 { let width = input
return Err(Error::custom(format!( .next()
"InvalidInput: Expected input format: [width]x[height] (ex. 1920x1080), got {:?}", .ok_or_else(|| Error::custom("missing width for `Resolution` or an invalid input"))
input, .and_then(|v| v.parse().map_err(Error::parse_int))?;
)));
}
Ok(Self { let height = input
width: tokens[0].parse().map_err(Error::parse_int)?, .next()
height: tokens[1].parse().map_err(Error::parse_int)?, .ok_or_else(|| Error::custom("missing height for `Resolution` or an invalid input"))
}) .and_then(|v| v.parse().map_err(Error::parse_int))?;
Ok(Self { width, height })
} }
} }