use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; use libc::c_int; use std::borrow::Borrow; use std::convert::AsRef; use std::fmt; use std::iter; use std::marker::PhantomData; use std::mem; use std::ops::{Deref, DerefMut, Index, IndexMut, Range}; use crate::error::ErrorStack; use crate::util::ForeignTypeExt; use crate::{cvt, cvt_p, LenType}; cfg_if! { if #[cfg(ossl110)] { use ffi::{ OPENSSL_sk_pop, OPENSSL_sk_free, OPENSSL_sk_num, OPENSSL_sk_value, OPENSSL_STACK, OPENSSL_sk_new_null, OPENSSL_sk_push, }; } else { use ffi::{ sk_pop as OPENSSL_sk_pop, sk_free as OPENSSL_sk_free, sk_num as OPENSSL_sk_num, sk_value as OPENSSL_sk_value, _STACK as OPENSSL_STACK, sk_new_null as OPENSSL_sk_new_null, sk_push as OPENSSL_sk_push, }; } } /// Trait implemented by types which can be placed in a stack. /// /// It should not be implemented for any type outside of this crate. pub trait Stackable: ForeignType { /// The C stack type for this element. /// /// Generally called `stack_st_{ELEMENT_TYPE}`, normally hidden by the /// `STACK_OF(ELEMENT_TYPE)` macro in the OpenSSL API. type StackType; } /// An owned stack of `T`. pub struct Stack(*mut T::StackType); unsafe impl Send for Stack {} unsafe impl Sync for Stack {} impl fmt::Debug for Stack where T: Stackable, T::Ref: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_list().entries(self).finish() } } impl Drop for Stack { fn drop(&mut self) { unsafe { while self.pop().is_some() {} OPENSSL_sk_free(self.0 as *mut _); } } } impl Stack { pub fn new() -> Result, ErrorStack> { unsafe { ffi::init(); let ptr = cvt_p(OPENSSL_sk_new_null())?; Ok(Stack(ptr as *mut _)) } } } impl iter::IntoIterator for Stack { type IntoIter = IntoIter; type Item = T; fn into_iter(self) -> IntoIter { let it = IntoIter { stack: self.0, idxs: 0..self.len() as LenType, }; mem::forget(self); it } } impl AsRef> for Stack { fn as_ref(&self) -> &StackRef { self } } impl Borrow> for Stack { fn borrow(&self) -> &StackRef { self } } impl ForeignType for Stack { type CType = T::StackType; type Ref = StackRef; #[inline] unsafe fn from_ptr(ptr: *mut T::StackType) -> Stack { assert!( !ptr.is_null(), "Must not instantiate a Stack from a null-ptr - use Stack::new() in \ that case" ); Stack(ptr) } #[inline] fn as_ptr(&self) -> *mut T::StackType { self.0 } } impl Deref for Stack { type Target = StackRef; fn deref(&self) -> &StackRef { unsafe { StackRef::from_ptr(self.0) } } } impl DerefMut for Stack { fn deref_mut(&mut self) -> &mut StackRef { unsafe { StackRef::from_ptr_mut(self.0) } } } pub struct IntoIter { stack: *mut T::StackType, idxs: Range, } impl Drop for IntoIter { fn drop(&mut self) { unsafe { // https://github.com/rust-lang/rust-clippy/issues/7510 #[allow(clippy::while_let_on_iterator)] while let Some(_) = self.next() {} OPENSSL_sk_free(self.stack as *mut _); } } } impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { unsafe { self.idxs .next() .map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _)) } } fn size_hint(&self) -> (usize, Option) { self.idxs.size_hint() } } impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { unsafe { self.idxs .next_back() .map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _)) } } } impl ExactSizeIterator for IntoIter {} pub struct StackRef(Opaque, PhantomData); unsafe impl Send for StackRef {} unsafe impl Sync for StackRef {} impl ForeignTypeRef for StackRef { type CType = T::StackType; } impl StackRef { fn as_stack(&self) -> *mut OPENSSL_STACK { self.as_ptr() as *mut _ } /// Returns the number of items in the stack. pub fn len(&self) -> usize { unsafe { OPENSSL_sk_num(self.as_stack()) as usize } } /// Determines if the stack is empty. pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn iter(&self) -> Iter<'_, T> { Iter { stack: self, idxs: 0..self.len() as LenType, } } pub fn iter_mut(&mut self) -> IterMut<'_, T> { IterMut { idxs: 0..self.len() as LenType, stack: self, } } /// Returns a reference to the element at the given index in the /// stack or `None` if the index is out of bounds pub fn get(&self, idx: usize) -> Option<&T::Ref> { unsafe { if idx >= self.len() { return None; } Some(T::Ref::from_ptr(self._get(idx))) } } /// Returns a mutable reference to the element at the given index in the /// stack or `None` if the index is out of bounds pub fn get_mut(&mut self, idx: usize) -> Option<&mut T::Ref> { unsafe { if idx >= self.len() { return None; } Some(T::Ref::from_ptr_mut(self._get(idx))) } } /// Pushes a value onto the top of the stack. pub fn push(&mut self, data: T) -> Result<(), ErrorStack> { unsafe { cvt(OPENSSL_sk_push(self.as_stack(), data.as_ptr() as *mut _) as c_int)?; mem::forget(data); Ok(()) } } /// Removes the last element from the stack and returns it. pub fn pop(&mut self) -> Option { unsafe { let ptr = OPENSSL_sk_pop(self.as_stack()); T::from_ptr_opt(ptr as *mut _) } } unsafe fn _get(&self, idx: usize) -> *mut T::CType { OPENSSL_sk_value(self.as_stack(), idx as LenType) as *mut _ } } impl Index for StackRef { type Output = T::Ref; fn index(&self, index: usize) -> &T::Ref { self.get(index).unwrap() } } impl IndexMut for StackRef { fn index_mut(&mut self, index: usize) -> &mut T::Ref { self.get_mut(index).unwrap() } } impl<'a, T: Stackable> iter::IntoIterator for &'a StackRef { type Item = &'a T::Ref; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Iter<'a, T> { self.iter() } } impl<'a, T: Stackable> iter::IntoIterator for &'a mut StackRef { type Item = &'a mut T::Ref; type IntoIter = IterMut<'a, T>; fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } } impl<'a, T: Stackable> iter::IntoIterator for &'a Stack { type Item = &'a T::Ref; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Iter<'a, T> { self.iter() } } impl<'a, T: Stackable> iter::IntoIterator for &'a mut Stack { type Item = &'a mut T::Ref; type IntoIter = IterMut<'a, T>; fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } } /// An iterator over the stack's contents. pub struct Iter<'a, T: Stackable> { stack: &'a StackRef, idxs: Range, } impl<'a, T: Stackable> Iterator for Iter<'a, T> { type Item = &'a T::Ref; fn next(&mut self) -> Option<&'a T::Ref> { unsafe { self.idxs .next() .map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) } } fn size_hint(&self) -> (usize, Option) { self.idxs.size_hint() } } impl<'a, T: Stackable> DoubleEndedIterator for Iter<'a, T> { fn next_back(&mut self) -> Option<&'a T::Ref> { unsafe { self.idxs .next_back() .map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) } } } impl<'a, T: Stackable> ExactSizeIterator for Iter<'a, T> {} /// A mutable iterator over the stack's contents. pub struct IterMut<'a, T: Stackable> { stack: &'a mut StackRef, idxs: Range, } impl<'a, T: Stackable> Iterator for IterMut<'a, T> { type Item = &'a mut T::Ref; fn next(&mut self) -> Option<&'a mut T::Ref> { unsafe { self.idxs .next() .map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) } } fn size_hint(&self) -> (usize, Option) { self.idxs.size_hint() } } impl<'a, T: Stackable> DoubleEndedIterator for IterMut<'a, T> { fn next_back(&mut self) -> Option<&'a mut T::Ref> { unsafe { self.idxs .next_back() .map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) } } } impl<'a, T: Stackable> ExactSizeIterator for IterMut<'a, T> {}