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
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]
}
}