diff --git a/.gitignore b/.gitignore index 00d0128..ac44273 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ # Generated by Cargo /Cargo.lock /target +.idea + diff --git a/src/arrayvec.rs b/src/arrayvec.rs index e87b3ef..a7b1436 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -206,6 +206,22 @@ impl ArrayVec { ArrayVecImpl::try_push(self, element) } + /// Push a new uninitialised value to the end of the vector and return + /// a mutable reference to it. + /// + /// This is useful if the backed value is large and won't fit on stack. + /// + /// Capacity error carries the data - since we don't have data then we need a different error type. + pub unsafe fn try_push_uninit(&mut self) -> Result<*mut T, ()> { + let len = self.len(); + if len >= Self::CAPACITY { + return Err(()) + } + let new_ptr = self.as_mut_ptr().add(len); + self.set_len(len + 1); + Ok(new_ptr) + } + /// Push `element` to the end of the vector without checking the capacity. /// /// It is up to the caller to ensure the capacity of the vector is diff --git a/tests/tests.rs b/tests/tests.rs index 16508b7..5321134 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -7,7 +7,7 @@ use std::mem; use arrayvec::CapacityError; use std::collections::HashMap; - +use std::sync::RwLock; #[test] fn test_simple() { @@ -777,3 +777,50 @@ fn test_arraystring_zero_filled_has_some_sanity_checks() { assert_eq!(string.as_str(), "\0\0\0\0"); assert_eq!(string.len(), 4); } + +/// 3 MB struct +struct ReallyBigStruct { + pub(crate) field_one: [u8; 1_000_000], + pub(crate) field_two: [u8; 1_000_000], + pub(crate) field_three: [u8; 1_000_000], +} + +/// Const initialised struct outside of stack +/// We need to initialise this outside of the stack, since otherwise there is a memory copy from +/// the stack into the heap. +/// With a static initialisation, we do not have a stack copy. +static large_struct: RwLock> = RwLock::new(ArrayVec::new_const()); + +#[test] +fn test_push_uninit() { + let mut lock = large_struct.write().unwrap(); + let ptr = unsafe { lock.try_push_uninit().unwrap() }; + let field_one = unsafe {&mut (*ptr).field_one}; + *field_one = [1; 1_000_000]; + let field_two = unsafe {&mut (*ptr).field_two}; + *field_two = [2; 1_000_000]; + let field_three = unsafe {&mut (*ptr).field_three}; + *field_three = [3; 1_000_000]; + + assert_eq!(lock.len(), 1); + assert_eq!(lock[0].field_one[3], 1); + assert_eq!(lock[0].field_two[999], 2); + assert_eq!(lock[0].field_three[999_999], 3); + + // Push a second value + let ptr = unsafe { lock.try_push_uninit().unwrap() }; + let field_one = unsafe {&mut (*ptr).field_one}; + *field_one = [4; 1_000_000]; + let field_two = unsafe {&mut (*ptr).field_two}; + *field_two = [5; 1_000_000]; + let field_three = unsafe {&mut (*ptr).field_three}; + *field_three = [6; 1_000_000]; + + assert_eq!(lock.len(), 2); + assert_eq!(lock[0].field_one[3], 1); + assert_eq!(lock[0].field_two[999], 2); + assert_eq!(lock[0].field_three[999_999], 3); + assert_eq!(lock[1].field_one[3], 4); + assert_eq!(lock[1].field_two[999], 5); + assert_eq!(lock[1].field_three[999_999], 6); +}