From 096957b1671a2cfd77efa45d7b5829381405f963 Mon Sep 17 00:00:00 2001 From: Luro02 <24826124+Luro02@users.noreply.github.com> Date: Sat, 18 Apr 2020 12:50:45 +0200 Subject: [PATCH] Add `stable-vec` #52 --- Cargo.toml | 1 + src/lib.rs | 1 + src/media_playlist.rs | 102 ++++++++++++++++++++---------------------- src/traits.rs | 12 +++++ 4 files changed, 62 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 88cc95c..580f697 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ thiserror = "1.0" derive_more = "0.99" shorthand = "0.1" strum = { version = "0.17", features = ["derive"] } +stable-vec = { version = "0.4" } [dev-dependencies] pretty_assertions = "0.6" diff --git a/src/lib.rs b/src/lib.rs index 7fc1ac9..65bfe30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,4 +136,5 @@ mod media_segment; mod traits; pub use error::Result; +pub use stable_vec; pub use traits::*; diff --git a/src/media_playlist.rs b/src/media_playlist.rs index eb89d75..80068ea 100644 --- a/src/media_playlist.rs +++ b/src/media_playlist.rs @@ -1,9 +1,10 @@ -use std::collections::{BTreeMap, HashSet}; +use std::collections::HashSet; use std::fmt; use std::str::FromStr; use std::time::Duration; use derive_builder::Builder; +use stable_vec::StableVec; use crate::line::{Line, Lines, Tag}; use crate::media_segment::MediaSegment; @@ -19,7 +20,7 @@ use crate::utils::{tag, BoolExt}; use crate::{Error, RequiredVersion}; /// Media playlist. -#[derive(Builder, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Builder, Debug, Clone, PartialEq, Eq)] #[builder(build_fn(skip), setter(strip_option))] #[non_exhaustive] pub struct MediaPlaylist { @@ -105,7 +106,7 @@ pub struct MediaPlaylist { /// /// This field is required. #[builder(setter(custom))] - pub segments: BTreeMap, + pub segments: StableVec, /// The allowable excess duration of each media segment in the /// associated playlist. /// @@ -225,17 +226,15 @@ impl MediaPlaylistBuilder { /// Adds a media segment to the resulting playlist and assigns the next free /// [`MediaSegment::number`] to the segment. pub fn push_segment(&mut self, segment: MediaSegment) -> &mut Self { - let segments = self.segments.get_or_insert_with(BTreeMap::new); + let segments = self.segments.get_or_insert_with(StableVec::new); - let number = { - if segment.explicit_number { - segment.number - } else { - segments.keys().last().copied().unwrap_or(0) + 1 - } - }; + if segment.explicit_number { + segments.reserve_for(segment.number); + segments.insert(segment.number, segment); + } else { + segments.push(segment); + } - segments.insert(number, segment); self } @@ -255,11 +254,22 @@ impl MediaPlaylistBuilder { /// will be present in the final media playlist and the following is only /// possible if the segment is marked with `ExtXDiscontinuity`. pub fn segments(&mut self, segments: Vec) -> &mut Self { - // media segments are numbered starting at either 0 or the discontinuity - // sequence, but it might not be available at the moment. - // - // -> final numbering will be applied in the build function - self.segments = Some(segments.into_iter().enumerate().collect()); + let mut vec = StableVec::::with_capacity(segments.len()); + let mut remaining = Vec::with_capacity(segments.len()); + + for segment in segments { + if segment.explicit_number { + vec.insert(segment.number, segment); + } else { + remaining.push(segment); + } + } + + for segment in remaining { + vec.push(segment); + } + + self.segments = Some(vec); self } @@ -274,20 +284,14 @@ impl MediaPlaylistBuilder { let sequence_number = self.media_sequence.unwrap_or(0); - let segments = self + let mut segments = self .segments - .as_ref() + .clone() .ok_or_else(|| "missing field `segments`".to_string())?; - // insert all explictly numbered segments into the result - let mut result_segments = segments - .iter() - .filter_map(|(_, s)| s.explicit_number.athen(|| (s.number, s.clone()))) - .collect::>(); - // no segment should exist before the sequence_number - if let Some(first_segment) = result_segments.keys().min() { - if sequence_number > *first_segment { + if let Some(first_segment) = segments.find_first() { + if sequence_number > first_segment.number && first_segment.explicit_number { return Err(format!( "there should be no segment ({}) before the sequence_number ({})", first_segment, sequence_number, @@ -295,20 +299,14 @@ impl MediaPlaylistBuilder { } } - let mut position = sequence_number; let mut previous_range: Option = None; - for segment in segments - .iter() - .filter_map(|(_, s)| if s.explicit_number { None } else { Some(s) }) - { - while result_segments.contains_key(&position) { - position += 1; + for (i, segment) in segments.iter_mut() { + // assign the correct number to all implcitly numbered segments: + if !segment.explicit_number { + segment.number = i + sequence_number; } - let mut segment = segment.clone(); - segment.number = position; - // add the segment number as iv, if the iv is missing: for key in &mut segment.keys { if let ExtXKey(Some(DecryptionKey { @@ -340,21 +338,17 @@ impl MediaPlaylistBuilder { previous_range = segment.byte_range; } - - result_segments.insert(segment.number, segment); - position += 1; } - let mut previous_n = None; - - for n in result_segments.keys() { - if let Some(previous_n) = previous_n { - if previous_n + 1 != *n { - return Err(format!("missing segment ({})", previous_n + 1)); - } - } - - previous_n = Some(n); + // TODO: can segments be missing? + if !segments.is_compact() { + // find the missing segment by iterating through all segments: + // let missing = segments + // .iter() + // .enumerate() + // .find_map(|(i, e)| e.is_none().athen(i)) + // .unwrap(); + return Err(format!("a segment is missing")); } Ok(MediaPlaylist { @@ -368,7 +362,7 @@ impl MediaPlaylistBuilder { has_independent_segments: self.has_independent_segments.unwrap_or(false), start: self.start.unwrap_or(None), has_end_list: self.has_end_list.unwrap_or(false), - segments: result_segments, + segments, allowable_excess_duration: self .allowable_excess_duration .unwrap_or_else(|| Duration::from_secs(0)), @@ -816,9 +810,9 @@ mod tests { .build() .unwrap(); let mut segments = playlist.segments.into_iter().map(|(k, v)| (k, v.number)); - assert_eq!(segments.next(), Some((2680, 2680))); - assert_eq!(segments.next(), Some((2681, 2681))); - assert_eq!(segments.next(), Some((2682, 2682))); + assert_eq!(segments.next(), Some((0, 2680))); + assert_eq!(segments.next(), Some((1, 2681))); + assert_eq!(segments.next(), Some((2, 2682))); assert_eq!(segments.next(), None); } diff --git a/src/traits.rs b/src/traits.rs index dd459a3..95a17bf 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,5 +1,7 @@ use std::collections::{BTreeMap, HashMap}; +use stable_vec::StableVec; + use crate::types::{DecryptionKey, ProtocolVersion}; mod private { @@ -108,6 +110,16 @@ impl RequiredVersion for HashMap { } } +impl RequiredVersion for StableVec { + fn required_version(&self) -> ProtocolVersion { + self.values() + .map(RequiredVersion::required_version) + .max() + // return ProtocolVersion::V1, if the iterator is empty: + .unwrap_or_default() + } +} + #[cfg(test)] mod tests { use super::*;