// Copyright (C) 2017 Sebastian Dröge // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use glib; use glib::translate::*; use glib::value::{FromValueOptional, ToValue}; use glib::StaticType; use glib::Value; use glib_sys; use glib_sys::{gconstpointer, gpointer}; use gobject_sys; use gst_sys; use std::error::Error; use std::ffi::CString; use std::fmt; use std::iter; use std::marker::PhantomData; use std::mem; use std::ptr; use std::sync::Arc; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum IteratorError { Resync, Error, } impl fmt::Display for IteratorError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { IteratorError::Resync => write!(f, "Resync"), IteratorError::Error => write!(f, "Error"), } } } impl Error for IteratorError { fn description(&self) -> &str { match *self { IteratorError::Resync => "Resync", IteratorError::Error => "Error", } } } // Implemented manually so that we can use generics for the item pub struct Iterator { iter: ptr::NonNull, borrowed: bool, phantom: PhantomData, } impl Iterator where for<'a> T: FromValueOptional<'a> + 'static, { #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Result, IteratorError> { unsafe { let mut value = Value::uninitialized(); let res = gst_sys::gst_iterator_next(self.to_glib_none_mut().0, value.to_glib_none_mut().0); match res { gst_sys::GST_ITERATOR_OK => match value.get::().expect("Iterator::next") { Some(value) => Ok(Some(value)), None => Err(IteratorError::Error), }, gst_sys::GST_ITERATOR_DONE => Ok(None), gst_sys::GST_ITERATOR_RESYNC => Err(IteratorError::Resync), gst_sys::GST_ITERATOR_ERROR | _ => Err(IteratorError::Error), } } } pub fn resync(&mut self) { unsafe { gst_sys::gst_iterator_resync(self.to_glib_none_mut().0); } } pub fn filter(self, func: F) -> Self where F: Fn(T) -> bool + Send + Sync + 'static, { unsafe { let it = self.to_glib_none().0; mem::forget(self); let func_box: Box bool + Send + Sync + 'static> = Box::new(func); let mut closure_value = glib::Value::from_type(from_glib(filter_boxed_get_type::())); gobject_sys::g_value_take_boxed( closure_value.to_glib_none_mut().0, Arc::into_raw(Arc::new(func_box)) as gpointer, ); from_glib_full(gst_sys::gst_iterator_filter( it as *mut _, Some(filter_trampoline::), closure_value.to_glib_none().0, )) } } pub fn find(&mut self, func: F) -> Option where F: FnMut(T) -> bool, { unsafe { let mut elem = glib::Value::uninitialized(); let mut func = func; let func_ptr = &mut func as *mut F as gpointer; let res = from_glib(gst_sys::gst_iterator_find_custom( self.to_glib_none_mut().0, Some(find_trampoline::), elem.to_glib_none_mut().0, func_ptr, )); if res { elem.get::().expect("Iterator::find") } else { None } } } pub fn foreach(&mut self, func: F) -> Result<(), IteratorError> where F: FnMut(T), { unsafe { let mut func = func; let func_ptr = &mut func as *mut F as gpointer; let res = gst_sys::gst_iterator_foreach( self.to_glib_none_mut().0, Some(foreach_trampoline::), func_ptr, ); match res { gst_sys::GST_ITERATOR_OK | gst_sys::GST_ITERATOR_DONE => Ok(()), gst_sys::GST_ITERATOR_RESYNC => Err(IteratorError::Resync), gst_sys::GST_ITERATOR_ERROR | _ => Err(IteratorError::Error), } } } pub fn fold(&mut self, init: U, func: F) -> Result where F: FnMut(U, T) -> Result, { unsafe { let mut func = func; let func_ptr = &mut func as *mut F as gpointer; let mut accum = Some(init); let mut ret = glib::Value::from_type(glib::Type::Pointer); gobject_sys::g_value_set_pointer( ret.to_glib_none_mut().0, &mut accum as *mut _ as gpointer, ); let res = gst_sys::gst_iterator_fold( self.to_glib_none_mut().0, Some(fold_trampoline::), ret.to_glib_none_mut().0, func_ptr, ); match res { gst_sys::GST_ITERATOR_OK | gst_sys::GST_ITERATOR_DONE => Ok(accum.unwrap()), gst_sys::GST_ITERATOR_RESYNC => Err(IteratorError::Resync), gst_sys::GST_ITERATOR_ERROR | _ => Err(IteratorError::Error), } } } } impl Iterator where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { pub fn new>(imp: I) -> Self { static DUMMY_COOKIE: u32 = 0; unsafe { let it = gst_sys::gst_iterator_new( mem::size_of::>() as u32, T::static_type().to_glib(), ptr::null_mut(), &DUMMY_COOKIE as *const _ as *mut _, Some(rs_iterator_copy::), Some(rs_iterator_next::), None, Some(rs_iterator_resync::), Some(rs_iterator_free::), ); { let it = it as *mut RsIterator; (*it).imp = Some(imp); } from_glib_full(it) } } } impl Iterator where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Clone + Send + 'static, { pub fn from_vec(items: Vec) -> Self { Self::new(VecIteratorImpl::new(items)) } } #[repr(C)] struct RsIterator> where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { iter: gst_sys::GstIterator, imp: Option, phantom: PhantomData, } pub trait IteratorImpl: Clone + Send + 'static where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { fn next(&mut self) -> Option>; fn resync(&mut self); } unsafe extern "C" fn rs_iterator_copy>( it: *const gst_sys::GstIterator, copy: *mut gst_sys::GstIterator, ) where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { let it = it as *const RsIterator; let copy = copy as *mut RsIterator; ptr::write(&mut (*copy).imp, (*it).imp.clone()); } unsafe extern "C" fn rs_iterator_free>(it: *mut gst_sys::GstIterator) where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { let it = it as *mut RsIterator; let _ = (*it).imp.take(); } unsafe extern "C" fn rs_iterator_next>( it: *mut gst_sys::GstIterator, result: *mut gobject_sys::GValue, ) -> gst_sys::GstIteratorResult where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { let it = it as *mut RsIterator; match (*it).imp.as_mut().map(|imp| imp.next()).unwrap() { Some(Ok(value)) => { let value = value.to_value(); ptr::write(result, ptr::read(value.to_glib_none().0)); mem::forget(value); gst_sys::GST_ITERATOR_OK } None => gst_sys::GST_ITERATOR_DONE, Some(Err(res)) => match res { IteratorError::Resync => gst_sys::GST_ITERATOR_RESYNC, IteratorError::Error => gst_sys::GST_ITERATOR_ERROR, }, } } unsafe extern "C" fn rs_iterator_resync>(it: *mut gst_sys::GstIterator) where for<'a> T: FromValueOptional<'a> + StaticType + ToValue + Send + 'static, { let it = it as *mut RsIterator; (*it).imp.as_mut().map(|imp| imp.resync()).unwrap(); } #[derive(Clone)] struct VecIteratorImpl { pos: usize, items: Vec, } impl VecIteratorImpl where for<'a> T: StaticType + ToValue + FromValueOptional<'a> + Clone + Send + 'static, { fn new(items: Vec) -> Self { Self { pos: 0, items } } } impl IteratorImpl for VecIteratorImpl where for<'a> T: StaticType + ToValue + FromValueOptional<'a> + Clone + Send + 'static, { fn next(&mut self) -> Option> { if self.pos < self.items.len() { let res = Ok(self.items[self.pos].clone()); self.pos += 1; return Some(res); } None } fn resync(&mut self) { self.pos = 0; } } unsafe impl Send for Iterator {} unsafe impl Sync for Iterator {} unsafe extern "C" fn filter_trampoline(value: gconstpointer, func: gconstpointer) -> i32 where for<'a> T: FromValueOptional<'a> + 'static, { let value = value as *const gobject_sys::GValue; let func = func as *const gobject_sys::GValue; let func = gobject_sys::g_value_get_boxed(func); #[allow(clippy::transmute_ptr_to_ref)] let func: &&(dyn Fn(T) -> bool + Send + Sync + 'static) = mem::transmute(func); let value = &*(value as *const glib::Value); let value = value .get::() .expect("Iterator filter_trampoline") .unwrap(); if func(value) { 0 } else { -1 } } unsafe extern "C" fn filter_boxed_ref(boxed: gpointer) -> gpointer { let boxed = Arc::from_raw(boxed as *const Box bool + Send + Sync + 'static>); let copy = Arc::clone(&boxed); // Forget it and keep it alive, we will still need it later let _ = Arc::into_raw(boxed); Arc::into_raw(copy) as gpointer } unsafe extern "C" fn filter_boxed_unref(boxed: gpointer) { let _ = Arc::from_raw(boxed as *const Box bool + Send + Sync + 'static>); } unsafe extern "C" fn filter_boxed_get_type() -> glib_sys::GType { use std::sync::Once; static mut TYPE: glib_sys::GType = gobject_sys::G_TYPE_INVALID; static ONCE: Once = Once::new(); ONCE.call_once(|| { let type_name = { let mut idx = 0; loop { let type_name = CString::new(format!( "GstRsIteratorFilterBoxed-{}-{}", T::static_type().name(), idx )) .unwrap(); if gobject_sys::g_type_from_name(type_name.as_ptr()) == gobject_sys::G_TYPE_INVALID { break type_name; } idx += 1; } }; TYPE = gobject_sys::g_boxed_type_register_static( type_name.as_ptr(), Some(filter_boxed_ref::), Some(filter_boxed_unref::), ); }); TYPE } unsafe extern "C" fn find_trampoline bool>( value: gconstpointer, func: gconstpointer, ) -> i32 where for<'a> T: FromValueOptional<'a> + 'static, { let value = value as *const gobject_sys::GValue; let func = func as *mut F; let value = &*(value as *const glib::Value); let value = value.get::().expect("Iterator find_trampoline").unwrap(); if (*func)(value) { 0 } else { -1 } } unsafe extern "C" fn foreach_trampoline( value: *const gobject_sys::GValue, func: gpointer, ) where for<'a> T: FromValueOptional<'a> + 'static, { let func = func as *mut F; let value = &*(value as *const glib::Value); let value = value .get::() .expect("Iterator foreach_trampoline") .unwrap(); (*func)(value); } unsafe extern "C" fn fold_trampoline Result>( value: *const gobject_sys::GValue, ret: *mut gobject_sys::GValue, func: gpointer, ) -> glib_sys::gboolean where for<'a> T: FromValueOptional<'a> + 'static, { let func = func as *mut F; let value = &*(value as *const glib::Value); let value = value.get::().expect("Iterator fold_trampoline").unwrap(); let accum = &mut *(gobject_sys::g_value_get_pointer(ret) as *mut Option); match (*func)(accum.take().unwrap(), value) { Ok(next_accum) => { *accum = Some(next_accum); glib_sys::GTRUE } Err(next_accum) => { *accum = Some(next_accum); glib_sys::GFALSE } } } impl Clone for Iterator { fn clone(&self) -> Self { unsafe { from_glib_full(gst_sys::gst_iterator_copy(self.to_glib_none().0)) } } } impl fmt::Debug for Iterator { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Iterator") .field("iter", &self.iter) .field("borrowed", &self.borrowed) .finish() } } impl Drop for Iterator { fn drop(&mut self) { if !self.borrowed { unsafe { gst_sys::gst_iterator_free(self.iter.as_ptr()); } } } } impl iter::IntoIterator for Iterator where for<'a> T: FromValueOptional<'a> + 'static, { type Item = Result; type IntoIter = StdIterator; fn into_iter(self) -> Self::IntoIter { Self::IntoIter::new(self) } } impl glib::types::StaticType for Iterator { fn static_type() -> glib::types::Type { unsafe { glib::translate::from_glib(gst_sys::gst_iterator_get_type()) } } } #[doc(hidden)] impl<'a, T: StaticType> glib::value::FromValueOptional<'a> for Iterator { unsafe fn from_value_optional(value: &glib::Value) -> Option { Option::>::from_glib_none( gobject_sys::g_value_get_boxed(value.to_glib_none().0) as *mut gst_sys::GstIterator, ) } } #[doc(hidden)] impl glib::value::SetValue for Iterator { unsafe fn set_value(value: &mut glib::Value, this: &Self) { gobject_sys::g_value_set_boxed( value.to_glib_none_mut().0, glib::translate::ToGlibPtr::<*const gst_sys::GstIterator>::to_glib_none(this).0 as glib_sys::gpointer, ) } } #[doc(hidden)] impl glib::value::SetValueOptional for Iterator { unsafe fn set_value_optional(value: &mut glib::Value, this: Option<&Self>) { gobject_sys::g_value_set_boxed( value.to_glib_none_mut().0, glib::translate::ToGlibPtr::<*const gst_sys::GstIterator>::to_glib_none(&this).0 as glib_sys::gpointer, ) } } #[doc(hidden)] impl glib::translate::GlibPtrDefault for Iterator { type GlibType = *mut gst_sys::GstIterator; } #[doc(hidden)] impl<'a, T: 'static> glib::translate::ToGlibPtr<'a, *const gst_sys::GstIterator> for Iterator { type Storage = &'a Iterator; fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const gst_sys::GstIterator, Self> { glib::translate::Stash(self.iter.as_ptr(), self) } fn to_glib_full(&self) -> *const gst_sys::GstIterator { unimplemented!() } } #[doc(hidden)] impl<'a, T: 'static> glib::translate::ToGlibPtrMut<'a, *mut gst_sys::GstIterator> for Iterator { type Storage = &'a mut Iterator; #[inline] fn to_glib_none_mut( &'a mut self, ) -> glib::translate::StashMut<'a, *mut gst_sys::GstIterator, Self> { glib::translate::StashMut(self.iter.as_ptr(), self) } } #[doc(hidden)] impl glib::translate::FromGlibPtrNone<*const gst_sys::GstIterator> for Iterator { #[inline] unsafe fn from_glib_none(ptr: *const gst_sys::GstIterator) -> Self { assert_ne!( gobject_sys::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib_sys::GFALSE ); from_glib_full(gst_sys::gst_iterator_copy(ptr)) } } #[doc(hidden)] impl glib::translate::FromGlibPtrNone<*mut gst_sys::GstIterator> for Iterator { #[inline] unsafe fn from_glib_none(ptr: *mut gst_sys::GstIterator) -> Self { assert_ne!( gobject_sys::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib_sys::GFALSE ); from_glib_full(gst_sys::gst_iterator_copy(ptr)) } } #[doc(hidden)] impl glib::translate::FromGlibPtrBorrow<*mut gst_sys::GstIterator> for Iterator { #[inline] unsafe fn from_glib_borrow(ptr: *mut gst_sys::GstIterator) -> Self { assert!(!ptr.is_null()); assert_ne!( gobject_sys::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib_sys::GFALSE ); Self { iter: ptr::NonNull::new_unchecked(ptr), borrowed: true, phantom: PhantomData, } } } #[doc(hidden)] impl glib::translate::FromGlibPtrFull<*mut gst_sys::GstIterator> for Iterator { #[inline] unsafe fn from_glib_full(ptr: *mut gst_sys::GstIterator) -> Self { assert!(!ptr.is_null()); assert_ne!( gobject_sys::g_type_is_a((*ptr).type_, T::static_type().to_glib()), glib_sys::GFALSE ); Self { iter: ptr::NonNull::new_unchecked(ptr), borrowed: false, phantom: PhantomData, } } } pub struct StdIterator { inner: Iterator, error: Option, } impl StdIterator { fn new(inner: Iterator) -> Self { Self { inner, error: None } } } impl Clone for StdIterator { fn clone(&self) -> Self { Self { inner: self.inner.clone(), error: self.error, } } } impl fmt::Debug for StdIterator { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("StdIterator") .field("inner", &self.inner) .field("error", &self.error) .finish() } } impl iter::Iterator for StdIterator where for<'a> T: FromValueOptional<'a> + 'static, { type Item = Result; fn next(&mut self) -> Option { match self.error { // Fuse the iterator after returning IteratorError::Error Some(IteratorError::Error) => return None, // The iterator needs a resync Some(IteratorError::Resync) => self.inner.resync(), None => {} } let res = self.inner.next(); self.error = res.as_ref().err().copied(); res.transpose() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_vec() { ::init().unwrap(); let vec = vec![1i32, 2, 3]; let mut it = Iterator::from_vec(vec); let val = it.next(); assert_eq!(val, Ok(Some(1))); let val = it.next(); assert_eq!(val, Ok(Some(2))); let val = it.next(); assert_eq!(val, Ok(Some(3))); assert_eq!(it.next(), Ok(None)); let vec = vec![1i32, 2, 3]; let mut it = Iterator::from_vec(vec); let mut vals = Vec::new(); while let Ok(Some(res)) = it.next() { vals.push(res); } assert_eq!(vals, [1, 2, 3]); } #[test] fn test_filter() { ::init().unwrap(); let vec = vec![1i32, 2, 3]; let mut it = Iterator::from_vec(vec).filter(|val| val % 2 == 1); let mut vals = Vec::new(); while let Ok(Some(res)) = it.next() { vals.push(res); } assert_eq!(vals, [1, 3]); } #[test] fn test_find() { ::init().unwrap(); // Our find let vec = vec![1i32, 2, 3]; let val = Iterator::from_vec(vec).find(|val| val == 2); assert_eq!(val.unwrap(), 2); } #[test] fn test_foreach() { ::init().unwrap(); let vec = vec![1i32, 2, 3]; let mut sum = 0; let res = Iterator::from_vec(vec).foreach(|val| sum += val); assert_eq!(res, Ok(())); assert_eq!(sum, 6); } #[test] fn test_fold() { ::init().unwrap(); // Our fold let vec = vec![1i32, 2, 3]; let res = Iterator::from_vec(vec).fold(0, |mut sum, val| { sum += val; Ok(sum) }); assert_eq!(res.unwrap(), 6); } #[test] fn test_std() { let mut it = Iterator::from_vec(vec![1i32, 2, 3]).into_iter(); assert_eq!(it.next(), Some(Ok(1))); assert_eq!(it.next(), Some(Ok(2))); assert_eq!(it.next(), Some(Ok(3))); assert_eq!(it.next(), None); } #[test] fn test_into_iter() { let mut v = vec![1i32, 2, 3].into_iter(); for x in Iterator::from_vec(vec![1i32, 2, 3]) { assert_eq!(x.unwrap(), v.next().unwrap()); } assert_eq!(v.next(), None); } #[test] fn test_std_resync_collect() { use prelude::*; use std::collections::BTreeSet; ::init().unwrap(); let bin = ::Bin::new(None); let id1 = ::ElementFactory::make("identity", None).unwrap(); let id2 = ::ElementFactory::make("identity", None).unwrap(); bin.add(&id1).unwrap(); let mut it = bin.iterate_elements().into_iter(); assert_eq!(it.next().unwrap().unwrap(), id1); bin.add(&id2).unwrap(); let res = it.by_ref().collect::, _>>().unwrap_err(); assert_eq!(res, IteratorError::Resync); let mut elems = BTreeSet::new(); elems.insert(id1); elems.insert(id2); let res = it.by_ref().collect::, _>>().unwrap(); assert_eq!(res, elems); let res = it.collect::, _>>().unwrap(); assert!(res.is_empty()); } #[test] fn test_std_resync_find() { use prelude::*; ::init().unwrap(); let bin = ::Bin::new(None); let id1 = ::ElementFactory::make("identity", None).unwrap(); let id2 = ::ElementFactory::make("identity", None).unwrap(); bin.add(&id1).unwrap(); let mut it = bin.iterate_elements().into_iter(); assert_eq!(it.next().unwrap().unwrap(), id1); bin.add(&id2).unwrap(); let res = it.find(|x| x.as_ref() == Ok(&id1)); assert_eq!(res.unwrap().unwrap(), id1); } }