1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
use orx_split_vec::SplitVecGrowth;
use crate::ImpVec;
impl<T, G> ImpVec<T, G>
where
G: SplitVecGrowth<T>,
{
/// Appends an element to the back of a collection.
///
/// Unlike `std::vec::Vec` or `orx_split_vec::SplitVec`;
/// push operation for `ImpVec` does **not** require a mutable reference.
///
/// # Examples
///
/// ```
/// use orx_imp_vec::ImpVec;
///
/// let vec = ImpVec::default();
/// vec.push(1);
/// vec.push(2);
///
/// // since push does not require a mut reference,
/// // it is legal to hold on to other immutable references
/// // while pushing elements.
/// let ref_elem = &vec[1];
/// let ref_elem_addr = ref_elem as *const i32;
/// assert_eq!(2, *ref_elem);
///
/// vec.push(3);
/// vec.push(4);
/// vec.push(5);
///
/// assert_eq!(2, *ref_elem);
/// assert_eq!(vec, [1, 2, 3, 4, 5]);
///
/// let ref_elem_addr_after_growth = &vec[1] as *const i32;
/// assert_eq!(ref_elem_addr, ref_elem_addr_after_growth);
/// ```
pub fn push(&self, value: T) {
let split_vec = unsafe { &mut *self.as_mut_ptr() };
let fragments = unsafe { split_vec.fragments_mut() };
if fragments.is_empty() {
self.add_fragment_with_first_value(value);
} else {
let last_f = fragments.len() - 1;
if fragments[last_f].has_capacity_for_one() {
fragments[last_f].push(value);
} else {
self.add_fragment_with_first_value(value);
}
};
}
/// Appends an element to the back of a collection and returns a reference to it.
///
/// The reference will always be valid unless the collection is mutated;
/// note that methods that grows the vector do **not** require a mutable reference,
/// such as, `push`, `push_get_ref`, `extend` or `extend_from_slice` methods.
///
/// # Examples
///
/// Hold on to valid references while pushing new items,
/// as long as the collection is not mutated with methods such as `insert`, `remove` or `pop`.
///
/// ```
/// use orx_imp_vec::ImpVec;
///
/// let vec = ImpVec::with_linear_growth(4);
/// let ref1 = vec.push_get_ref(1);
/// let ref_elem_addr = ref1 as *const i32;
///
/// vec.push(2);
/// vec.push(3);
/// let ref4 = vec.push_get_ref(4);
///
/// // capacity is expaneded here from 4 to 8; however, in chunks;
/// // therefore, data is not moved around and the references remain valid.
/// let ref5 = vec.push_get_ref(5);
///
///
/// assert_eq!(ref1, &1);
/// assert_eq!(ref4, &4);
/// assert_eq!(ref5, &5);
/// assert_eq!(vec, [1, 2, 3, 4, 5]);
///
/// let ref_elem_addr_after_growth = &vec[0] as *const i32;
/// assert_eq!(ref_elem_addr, ref_elem_addr_after_growth);
/// ```
///
/// As you may see below, any mutable method that can possibly invalidate the references
/// are not allowed.
///
/// ```
/// use orx_imp_vec::ImpVec;
///
/// let mut vec = ImpVec::default(); // mut required for the `insert`
/// let ref1 = vec.push_get_ref(1);
/// vec.push(2);
/// vec.push(3);
///
/// assert_eq!(ref1, &1);
/// assert_eq!(vec, [1, 2, 3]);
///
/// vec.insert(0, 42);
/// assert_eq!(vec, [42, 1, 2, 3]);
///
/// // below line does not compile as the 'insert' call breaks reference 'ref1'
/// // let value1 = *ref1;
/// ```
pub fn push_get_ref(&self, value: T) -> &T {
let split_vec = unsafe { &mut *self.as_mut_ptr() };
let fragments = unsafe { split_vec.fragments_mut() };
let f = if fragments.is_empty() {
self.add_fragment_with_first_value(value);
0
} else {
let last_f = fragments.len() - 1;
if fragments[last_f].has_capacity_for_one() {
fragments[last_f].push(value);
last_f
} else {
self.add_fragment_with_first_value(value);
last_f + 1
}
};
&fragments[f][fragments[f].len() - 1]
}
}
#[cfg(test)]
mod tests {
use crate::{test_all_growth_types, ImpVec};
use orx_split_vec::SplitVecGrowth;
#[test]
fn push() {
fn test<G: SplitVecGrowth<usize>>(vec: ImpVec<usize, G>) {
for i in 0..462 {
vec.push(i);
}
for i in 0..462 {
assert_eq!(i, vec[i]);
}
}
test_all_growth_types!(test);
}
#[test]
fn push_get_ref() {
fn test<G: SplitVecGrowth<usize>>(vec: ImpVec<usize, G>) {
let mut refs = Vec::with_capacity(4062);
let mut addr = Vec::with_capacity(4062);
for i in 0..4062 {
let refi = vec.push_get_ref(i);
refs.push(refi);
addr.push(refi as *const usize);
}
for i in 0..4062 {
assert_eq!(i, vec[i]);
assert_eq!(i, *refs[i]);
let curr_addr = &vec[i] as *const usize;
assert_eq!(curr_addr, addr[i]);
assert_eq!(i, unsafe { *addr[i] });
}
}
test_all_growth_types!(test);
}
}