diff --git a/src/types/key_format_versions.rs b/src/types/key_format_versions.rs index 214b725..494f4ac 100644 --- a/src/types/key_format_versions.rs +++ b/src/types/key_format_versions.rs @@ -1,8 +1,11 @@ +use std::cmp::Ordering; use std::fmt; +use std::hash::{Hash, Hasher}; +use std::iter::{Extend, FromIterator}; +use std::ops::{Index, IndexMut}; +use std::slice::SliceIndex; use std::str::FromStr; -use derive_more::{Deref, DerefMut}; - use crate::types::ProtocolVersion; use crate::utils::{quote, unquote}; use crate::Error; @@ -12,18 +15,49 @@ use crate::RequiredVersion; /// this instance complies with, if more than one version of a particular /// [`KeyFormat`] is defined. /// +/// ## Note on maximum size +/// +/// To reduce the memory usage and to make this struct implement [`Copy`], a +/// fixed size array is used internally (`[u8; 9]`), which can store a maximum +/// number of 9 `u8` numbers. +/// +/// If you encounter any m3u8 file, which fails to parse, because the buffer is +/// too small, feel free to [make an issue](https://github.com/sile/hls_m3u8/issues). +/// +/// ## Example +/// +/// ``` +/// use hls_m3u8::types::KeyFormatVersions; +/// +/// assert_eq!( +/// KeyFormatVersions::from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).to_string(), +/// "\"255/255/255/255/255/255/255/255/255\"".to_string() +/// ); +/// ``` +/// /// [`KeyFormat`]: crate::types::KeyFormat -#[derive(Deref, DerefMut, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct KeyFormatVersions(Vec); +#[derive(Debug, Clone, Copy)] +pub struct KeyFormatVersions { + // NOTE(Luro02): if the current array is not big enough one can easily increase + // the number of elements or change the type to something bigger, + // but it would be kinda wasteful to use a `Vec` here, which requires + // allocations and has a size of at least 24 bytes + // (::std::mem::size_of::>() = 24). + buffer: [u8; 9], + // Indicates the number of used items in the array. + len: u8, +} impl KeyFormatVersions { - /// Makes a new [`KeyFormatVersions`]. + /// Constructs an empty [`KeyFormatVersions`]. /// /// # Example /// /// ``` /// # use hls_m3u8::types::KeyFormatVersions; - /// let key_format_versions = KeyFormatVersions::new(); + /// let versions = KeyFormatVersions::new(); + /// + /// assert_eq!(versions, KeyFormatVersions::default()); /// ``` #[inline] #[must_use] @@ -31,40 +65,299 @@ impl KeyFormatVersions { /// Add a value to the end of [`KeyFormatVersions`]. /// + /// # Panics + /// + /// This function panics, if you try to push more elements, than + /// [`KeyFormatVersions::remaining`] returns. + /// /// # Example /// /// ``` /// # use hls_m3u8::types::KeyFormatVersions; - /// let mut key_format_versions = KeyFormatVersions::new(); + /// let mut versions = KeyFormatVersions::new(); /// - /// key_format_versions.push(1); + /// versions.push(1); + /// assert_eq!(versions, KeyFormatVersions::from([1])); /// ``` - pub fn push(&mut self, value: usize) { - if self.is_default() { - self.0 = vec![value]; + /// + /// This will panic, because it exceeded the maximum number of elements: + /// + /// ```{.should_panic} + /// # use hls_m3u8::types::KeyFormatVersions; + /// let mut versions = KeyFormatVersions::new(); + /// + /// for _ in 0..=versions.capacity() { + /// versions.push(1); // <- panics + /// } + /// ``` + pub fn push(&mut self, value: u8) { + if self.len as usize == self.buffer.len() { + panic!("reached maximum number of elements in KeyFormatVersions"); + } + + self.buffer[self.len()] = value; + self.len += 1; + } + + /// `KeyFormatVersions` has a limited capacity and this function returns how + /// many elements can be pushed, until it panics. + /// + /// # Example + /// + /// ``` + /// # use hls_m3u8::types::KeyFormatVersions; + /// let mut versions = KeyFormatVersions::new(); + /// + /// assert_eq!(versions.remaining(), versions.capacity()); + /// + /// versions.push(1); + /// versions.push(2); + /// versions.push(3); + /// assert_eq!(versions.remaining(), 6); + /// ``` + #[inline] + #[must_use] + pub fn remaining(&self) -> usize { self.capacity().saturating_sub(self.len()) } + + /// Returns the number of elements. + /// + /// # Example + /// + /// ``` + /// # use hls_m3u8::types::KeyFormatVersions; + /// let mut versions = KeyFormatVersions::new(); + /// + /// assert_eq!(versions.len(), 0); + /// + /// versions.push(2); + /// assert_eq!(versions.len(), 1); + /// ``` + #[inline] + #[must_use] + pub const fn len(&self) -> usize { self.len as usize } + + /// Returns the total number of elements that can be stored. + /// + /// # Note + /// + /// It should not be relied on that this function will always return 9. In + /// the future this number might increase. + #[inline] + #[must_use] + pub fn capacity(&self) -> usize { self.buffer.len() } + + /// Shortens the internal array to the provided length. + /// + /// # Note + /// + /// If `len` is greater than the current length, this has no effect. + /// + /// # Example + /// + /// ``` + /// # use hls_m3u8::types::KeyFormatVersions; + /// let mut versions = KeyFormatVersions::from([1, 2, 3, 4, 5, 6]); + /// versions.truncate(3); + /// + /// assert_eq!(versions, KeyFormatVersions::from([1, 2, 3])); + /// ``` + pub fn truncate(&mut self, len: usize) { + if len > self.len() { + return; + } + + self.len = len as u8; + } + + /// Returns `true` if there are no elements. + /// + /// # Example + /// + /// ``` + /// # use hls_m3u8::types::KeyFormatVersions; + /// let mut versions = KeyFormatVersions::new(); + /// + /// assert_eq!(versions.is_empty(), true); + /// + /// versions.push(2); + /// assert_eq!(versions.is_empty(), false); + /// ``` + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Removes the last element and returns it, or `None` if it is empty. + /// + /// # Example + /// + /// ``` + /// # use hls_m3u8::types::KeyFormatVersions; + /// let mut versions = KeyFormatVersions::new(); + /// + /// assert_eq!(versions.pop(), None); + /// + /// versions.push(2); + /// assert_eq!(versions.pop(), Some(2)); + /// assert_eq!(versions.is_empty(), true); + /// ``` + pub fn pop(&mut self) -> Option { + if self.is_empty() { + None } else { - self.0.push(value); + self.len -= 1; + Some(self.buffer[self.len()]) } } - /// Returns `true`, if [`KeyFormatVersions`] has the default value of - /// `vec![1]`. + /// Returns `true`, if it is either empty or has a length of 1 and the first + /// element is 1. /// /// # Example /// /// ``` /// # use hls_m3u8::types::KeyFormatVersions; - /// assert!(KeyFormatVersions::from(vec![1]).is_default()); + /// let mut versions = KeyFormatVersions::new(); + /// + /// assert_eq!(versions.is_default(), true); + /// + /// versions.push(1); + /// assert_eq!(versions.is_default(), true); + /// + /// assert_eq!(KeyFormatVersions::default().is_default(), true); /// ``` #[must_use] pub fn is_default(&self) -> bool { - // - self.0 == vec![1] && self.0.len() == 1 || self.0.is_empty() + self.is_empty() || (self.buffer[self.len().saturating_sub(1)] == 1 && self.len() == 1) + } +} + +impl PartialEq for KeyFormatVersions { + fn eq(&self, other: &Self) -> bool { + if self.len() == other.len() { + // only compare the parts in the buffer, that are used: + self.as_ref() == self.as_ref() + } else { + false + } + } +} + +impl Eq for KeyFormatVersions {} + +impl PartialOrd for KeyFormatVersions { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(::cmp(self, other)) + } +} + +impl Ord for KeyFormatVersions { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { self.as_ref().cmp(other.as_ref()) } +} + +impl Hash for KeyFormatVersions { + fn hash(&self, state: &mut H) { + state.write_usize(self.len()); + self.as_ref().hash(state); + } +} + +impl AsRef<[u8]> for KeyFormatVersions { + #[inline] + #[must_use] + fn as_ref(&self) -> &[u8] { &self.buffer[..self.len()] } +} + +impl AsMut<[u8]> for KeyFormatVersions { + #[inline] + #[must_use] + fn as_mut(&mut self) -> &mut [u8] { + // this temporary variable is required, because the compiler does not resolve + // the borrow to it's value immediately, so there is a shared borrow and + // therefore no exclusive borrow can be made. + let len = self.len(); + &mut self.buffer[..len] + } +} + +impl Extend for KeyFormatVersions { + fn extend>(&mut self, iter: I) { + for element in iter { + if self.remaining() == 0 { + break; + } + + self.push(element); + } + } +} + +impl<'a> Extend<&'a u8> for KeyFormatVersions { + fn extend>(&mut self, iter: I) { + >::extend(self, iter.into_iter().copied()) + } +} + +impl> Index for KeyFormatVersions { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { self.as_ref().index(index) } +} + +impl> IndexMut for KeyFormatVersions { + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { self.as_mut().index_mut(index) } +} + +impl IntoIterator for KeyFormatVersions { + type IntoIter = IntoIter; + type Item = u8; + + fn into_iter(self) -> Self::IntoIter { self.into() } +} + +impl FromIterator for KeyFormatVersions { + fn from_iter>(iter: I) -> Self { + let mut result = Self::default(); + // an array like [0; 9] as empty + let mut is_empty = true; + + for item in iter { + if item != 0 { + is_empty = false; + } + + if result.remaining() == 0 { + break; + } + + result.push(item); + } + + if is_empty { + return Self::default(); + } + + result + } +} + +impl<'a> FromIterator<&'a u8> for KeyFormatVersions { + fn from_iter>(iter: I) -> Self { + >::from_iter(iter.into_iter().copied()) } } impl Default for KeyFormatVersions { - fn default() -> Self { Self(vec![1]) } + #[inline] + fn default() -> Self { + Self { + buffer: [0; 9], + len: 0, + } + } } /// This tag requires [`ProtocolVersion::V5`]. @@ -76,41 +369,98 @@ impl FromStr for KeyFormatVersions { type Err = Error; fn from_str(input: &str) -> Result { - let mut result = unquote(input) + let mut result = Self::default(); + + for item in unquote(input) .split('/') .map(|v| v.parse().map_err(|e| Error::parse_int(v, e))) - .collect::, Error>>()?; + { + let item = item?; + + if result.remaining() == 0 { + return Err(Error::custom( + "reached maximum number of elements in KeyFormatVersions", + )); + } + + result.push(item); + } if result.is_empty() { result.push(1); } - Ok(Self(result)) + Ok(result) } } impl fmt::Display for KeyFormatVersions { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.is_default() { + if self.is_default() || self.is_empty() { return write!(f, "{}", quote("1")); } - if let Some(value) = self.0.iter().next() { - write!(f, "\"{}", value)?; + write!(f, "\"{}", self.buffer[0])?; - for value in self.0.iter().skip(1) { - write!(f, "/{}", value)?; - } - - write!(f, "\"")?; + for item in &self.buffer[1..self.len()] { + write!(f, "/{}", item)?; } + write!(f, "\"")?; + Ok(()) } } -impl> From for KeyFormatVersions { - fn from(value: I) -> Self { Self(value.into_iter().collect()) } +impl> From for KeyFormatVersions { + fn from(value: T) -> Self { Self::from_iter(value.as_ref().iter().map(|i| *i as u8)) } +} + +/// `Iterator` for [`KeyFormatVersions`]. +#[derive(Debug, Clone, PartialEq)] +pub struct IntoIter { + buffer: [T; 9], + position: usize, + len: usize, +} + +impl From for IntoIter { + fn from(value: KeyFormatVersions) -> Self { + Self { + buffer: value.buffer, + position: 0, + len: value.len(), + } + } +} + +impl<'a> From<&'a KeyFormatVersions> for IntoIter { + fn from(value: &'a KeyFormatVersions) -> Self { + Self { + buffer: value.buffer, + position: 0, + len: value.len(), + } + } +} + +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { self.len.saturating_sub(self.position) } +} + +impl ::core::iter::FusedIterator for IntoIter {} + +impl Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + if self.position == self.len { + return None; + } + + self.position += 1; + Some(self.buffer[self.position - 1]) + } } #[cfg(test)] @@ -118,27 +468,200 @@ mod tests { use super::*; use pretty_assertions::assert_eq; + #[test] + fn test_hash() { + let mut hasher_left = std::collections::hash_map::DefaultHasher::new(); + let mut hasher_right = std::collections::hash_map::DefaultHasher::new(); + + assert_eq!( + KeyFormatVersions::from([1, 2, 3]).hash(&mut hasher_left), + KeyFormatVersions::from([1, 2, 3]).hash(&mut hasher_right) + ); + + assert_eq!(hasher_left.finish(), hasher_right.finish()); + } + + #[test] + fn test_ord() { + assert_eq!( + KeyFormatVersions::from([1, 2]).cmp(&KeyFormatVersions::from([1, 2])), + Ordering::Equal + ); + + assert_eq!( + KeyFormatVersions::from([2]).cmp(&KeyFormatVersions::from([1, 2])), + Ordering::Greater + ); + + assert_eq!( + KeyFormatVersions::from([2, 3]).cmp(&KeyFormatVersions::from([1, 2])), + Ordering::Greater + ); + + assert_eq!( + KeyFormatVersions::from([]).cmp(&KeyFormatVersions::from([1, 2])), + Ordering::Less + ); + } + + #[test] + fn test_partial_eq() { + let mut versions = KeyFormatVersions::from([1, 2, 3, 4, 5, 6]); + versions.truncate(3); + + assert_eq!(versions, KeyFormatVersions::from([1, 2, 3])); + } + + #[test] + fn test_as_ref() { + assert_eq!(KeyFormatVersions::new().as_ref(), &[]); + assert_eq!(KeyFormatVersions::from([1, 2, 3]).as_ref(), &[1, 2, 3]); + assert_eq!(KeyFormatVersions::from([]).as_ref(), &[]); + } + + #[test] + fn test_as_mut() { + assert_eq!(KeyFormatVersions::new().as_mut(), &mut []); + assert_eq!(KeyFormatVersions::from([1, 2, 3]).as_mut(), &mut [1, 2, 3]); + assert_eq!(KeyFormatVersions::from([]).as_mut(), &mut []); + } + + #[test] + fn test_index() { + // test index + assert_eq!(&KeyFormatVersions::new()[..], &[]); + assert_eq!(&KeyFormatVersions::from([1, 2, 3])[..2], &[1, 2]); + assert_eq!(&KeyFormatVersions::from([1, 2, 3])[1..2], &[2]); + assert_eq!(&KeyFormatVersions::from([1, 2, 3])[..], &[1, 2, 3]); + + // test index_mut + assert_eq!(&mut KeyFormatVersions::new()[..], &mut []); + assert_eq!(&mut KeyFormatVersions::from([1, 2, 3])[..2], &mut [1, 2]); + assert_eq!(&mut KeyFormatVersions::from([1, 2, 3])[1..2], &mut [2]); + assert_eq!(&mut KeyFormatVersions::from([1, 2, 3])[..], &mut [1, 2, 3]); + } + + #[test] + fn test_extend() { + let mut versions = KeyFormatVersions::new(); + versions.extend(&[1, 2, 3]); + + assert_eq!(versions, KeyFormatVersions::from([1, 2, 3])); + + versions.extend(&[1, 2, 3]); + assert_eq!(versions, KeyFormatVersions::from([1, 2, 3, 1, 2, 3])); + + versions.extend(&[1, 2, 3, 4]); + assert_eq!( + versions, + KeyFormatVersions::from([1, 2, 3, 1, 2, 3, 1, 2, 3]) + ); + } + + #[test] + fn test_default() { + assert_eq!(KeyFormatVersions::default(), KeyFormatVersions::new()); + } + + #[test] + fn test_into_iter() { + assert_eq!(KeyFormatVersions::new().into_iter().next(), None); + assert_eq!(KeyFormatVersions::new().into_iter().len(), 0); + + let mut iterator = KeyFormatVersions::from([1, 2, 3, 4, 5]).into_iter(); + + assert_eq!(iterator.len(), 5); + assert_eq!(iterator.next(), Some(1)); + + assert_eq!(iterator.len(), 4); + assert_eq!(iterator.next(), Some(2)); + + assert_eq!(iterator.len(), 3); + assert_eq!(iterator.next(), Some(3)); + + assert_eq!(iterator.len(), 2); + assert_eq!(iterator.next(), Some(4)); + + assert_eq!(iterator.len(), 1); + assert_eq!(iterator.next(), Some(5)); + + assert_eq!(iterator.len(), 0); + assert_eq!(iterator.next(), None); + } + + #[test] + fn test_from_iter() { + assert_eq!( + { + let mut result = KeyFormatVersions::new(); + result.push(1); + result.push(2); + result.push(3); + result.push(4); + result + }, + KeyFormatVersions::from_iter(&[1, 2, 3, 4]) + ); + + assert_eq!( + { + let mut result = KeyFormatVersions::new(); + result.push(0); + result.push(1); + result.push(2); + result.push(3); + result.push(4); + result + }, + KeyFormatVersions::from_iter(&[0, 1, 2, 3, 4]) + ); + + assert_eq!(KeyFormatVersions::new(), KeyFormatVersions::from_iter(&[])); + + assert_eq!(KeyFormatVersions::new(), KeyFormatVersions::from_iter(&[0])); + assert_eq!( + KeyFormatVersions::new(), + KeyFormatVersions::from_iter(&[0, 0]) + ); + assert_eq!( + { + let mut result = KeyFormatVersions::new(); + result.push(0); + result.push(1); + result.push(2); + result.push(3); + result.push(4); + result.push(5); + result.push(6); + result.push(7); + result.push(8); + result + }, + KeyFormatVersions::from_iter(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) + ); + } + #[test] fn test_display() { assert_eq!( - KeyFormatVersions::from(vec![1, 2, 3, 4, 5]).to_string(), + KeyFormatVersions::from([1, 2, 3, 4, 5]).to_string(), quote("1/2/3/4/5") ); - assert_eq!(KeyFormatVersions::from(vec![]).to_string(), quote("1")); + assert_eq!(KeyFormatVersions::from([]).to_string(), quote("1")); assert_eq!(KeyFormatVersions::new().to_string(), quote("1")); } #[test] fn test_parser() { assert_eq!( - KeyFormatVersions::from(vec![1, 2, 3, 4, 5]), + KeyFormatVersions::from([1, 2, 3, 4, 5]), quote("1/2/3/4/5").parse().unwrap() ); - assert_eq!(KeyFormatVersions::from(vec![1]), "1".parse().unwrap()); + assert_eq!(KeyFormatVersions::from([1]), "1".parse().unwrap()); + assert_eq!(KeyFormatVersions::from([1, 2]), "1/2".parse().unwrap()); - assert_eq!(KeyFormatVersions::from(vec![1, 2]), "1/2".parse().unwrap()); assert!("1/b".parse::().is_err()); } @@ -152,28 +675,20 @@ mod tests { #[test] fn test_is_default() { - assert!(KeyFormatVersions::new().is_default()); - assert!(KeyFormatVersions::from(vec![]).is_default()); - assert!(!KeyFormatVersions::from(vec![1, 2, 3]).is_default()); + assert_eq!(KeyFormatVersions::new().is_default(), true); + assert_eq!(KeyFormatVersions::default().is_default(), true); + + assert_eq!(KeyFormatVersions::from([]).is_default(), true); + assert_eq!(KeyFormatVersions::from([1]).is_default(), true); + + assert_eq!(KeyFormatVersions::from([1, 2, 3]).is_default(), false); } #[test] fn test_push() { - let mut key_format_versions = KeyFormatVersions::from(vec![]); - + let mut key_format_versions = KeyFormatVersions::new(); key_format_versions.push(2); - assert_eq!(KeyFormatVersions::from(vec![2]), key_format_versions); - } - #[test] - fn test_deref() { - assert!(!KeyFormatVersions::new().is_empty()); - } - - #[test] - fn test_deref_mut() { - let mut key_format_versions = KeyFormatVersions::from(vec![1, 2, 3]); - key_format_versions.pop(); - assert_eq!(key_format_versions, KeyFormatVersions::from(vec![1, 2])); + assert_eq!(KeyFormatVersions::from([2]), key_format_versions); } }