/rust/registry/src/index.crates.io-6f17d22bba15001f/zerovec-0.10.4/src/varzerovec/owned.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // This file is part of ICU4X. For terms of use, please see the file |
2 | | // called LICENSE at the top level of the ICU4X source tree |
3 | | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | | |
5 | | // The mutation operations in this file should panic to prevent undefined behavior |
6 | | #![allow(clippy::unwrap_used)] |
7 | | #![allow(clippy::expect_used)] |
8 | | #![allow(clippy::indexing_slicing)] |
9 | | #![allow(clippy::panic)] |
10 | | |
11 | | use super::*; |
12 | | use crate::ule::*; |
13 | | use alloc::boxed::Box; |
14 | | use alloc::vec::Vec; |
15 | | use core::any; |
16 | | use core::convert::TryInto; |
17 | | use core::marker::PhantomData; |
18 | | use core::ops::Deref; |
19 | | use core::ops::Range; |
20 | | use core::{fmt, ptr, slice}; |
21 | | |
22 | | use super::components::LENGTH_WIDTH; |
23 | | use super::components::MAX_INDEX; |
24 | | use super::components::MAX_LENGTH; |
25 | | use super::components::METADATA_WIDTH; |
26 | | |
27 | | /// A fully-owned [`VarZeroVec`]. This type has no lifetime but has the same |
28 | | /// internal buffer representation of [`VarZeroVec`], making it cheaply convertible to |
29 | | /// [`VarZeroVec`] and [`VarZeroSlice`]. |
30 | | /// |
31 | | /// The `F` type parameter is a [`VarZeroVecFormat`] (see its docs for more details), which can be used to select the |
32 | | /// precise format of the backing buffer with various size and performance tradeoffs. It defaults to [`Index16`]. |
33 | | pub struct VarZeroVecOwned<T: ?Sized, F = Index16> { |
34 | | marker: PhantomData<(Box<T>, F)>, |
35 | | // safety invariant: must parse into a valid VarZeroVecComponents |
36 | | entire_slice: Vec<u8>, |
37 | | } |
38 | | |
39 | | impl<T: ?Sized, F> Clone for VarZeroVecOwned<T, F> { |
40 | 0 | fn clone(&self) -> Self { |
41 | 0 | VarZeroVecOwned { |
42 | 0 | marker: self.marker, |
43 | 0 | entire_slice: self.entire_slice.clone(), |
44 | 0 | } |
45 | 0 | } |
46 | | } |
47 | | |
48 | | // The effect of a shift on the indices in the varzerovec. |
49 | | #[derive(PartialEq)] |
50 | | enum ShiftType { |
51 | | Insert, |
52 | | Replace, |
53 | | Remove, |
54 | | } |
55 | | |
56 | | impl<T: VarULE + ?Sized, F: VarZeroVecFormat> Deref for VarZeroVecOwned<T, F> { |
57 | | type Target = VarZeroSlice<T, F>; |
58 | 0 | fn deref(&self) -> &VarZeroSlice<T, F> { |
59 | 0 | self.as_slice() |
60 | 0 | } Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::zerovec::slice::ZeroSlice<icu_properties::props::Script>> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_properties::provider::names::NormalizedPropertyNameStr> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::zerovec::slice::ZeroSlice<icu_locid::subtags::region::Region>> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_locid_transform::provider::StrStrPairVarULE> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_locid_transform::provider::LanguageStrStrPairVarULE> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::ule::unvalidated::UnvalidatedStr> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<str> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<str> as core::ops::deref::Deref>::deref Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<_, _> as core::ops::deref::Deref>::deref |
61 | | } |
62 | | |
63 | | impl<T: VarULE + ?Sized, F> VarZeroVecOwned<T, F> { |
64 | | /// Construct an empty VarZeroVecOwned |
65 | 0 | pub fn new() -> Self { |
66 | 0 | Self { |
67 | 0 | marker: PhantomData, |
68 | 0 | entire_slice: Vec::new(), |
69 | 0 | } |
70 | 0 | } |
71 | | } |
72 | | |
73 | | impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecOwned<T, F> { |
74 | | /// Construct a VarZeroVecOwned from a [`VarZeroSlice`] by cloning the internal data |
75 | 0 | pub fn from_slice(slice: &VarZeroSlice<T, F>) -> Self { |
76 | 0 | Self { |
77 | 0 | marker: PhantomData, |
78 | 0 | entire_slice: slice.as_bytes().into(), |
79 | 0 | } |
80 | 0 | } Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_locid_transform::provider::LanguageStrStrPairVarULE>>::from_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<_, _>>::from_slice |
81 | | |
82 | | /// Construct a VarZeroVecOwned from a list of elements |
83 | 0 | pub fn try_from_elements<A>(elements: &[A]) -> Result<Self, &'static str> |
84 | 0 | where |
85 | 0 | A: EncodeAsVarULE<T>, |
86 | 0 | { |
87 | 0 | Ok(if elements.is_empty() { |
88 | 0 | Self::from_slice(VarZeroSlice::new_empty()) |
89 | | } else { |
90 | | Self { |
91 | 0 | marker: PhantomData, |
92 | 0 | // TODO(#1410): Rethink length errors in VZV. |
93 | 0 | entire_slice: components::get_serializable_bytes_non_empty::<T, A, F>(elements) |
94 | 0 | .ok_or( |
95 | 0 | "Attempted to build VarZeroVec out of elements that \ |
96 | 0 | cumulatively are larger than a u32 in size", |
97 | 0 | )?, |
98 | | } |
99 | | }) |
100 | 0 | } Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_locid_transform::provider::LanguageStrStrPairVarULE>>::try_from_elements::<icu_locid_transform::provider::LanguageStrStrPair> Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<_, _>>::try_from_elements::<_> |
101 | | |
102 | | /// Obtain this `VarZeroVec` as a [`VarZeroSlice`] |
103 | 0 | pub fn as_slice(&self) -> &VarZeroSlice<T, F> { |
104 | 0 | let slice: &[u8] = &self.entire_slice; |
105 | 0 | unsafe { |
106 | 0 | // safety: the slice is known to come from a valid parsed VZV |
107 | 0 | VarZeroSlice::from_byte_slice_unchecked(slice) |
108 | 0 | } |
109 | 0 | } Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::zerovec::slice::ZeroSlice<icu_properties::props::Script>>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_properties::provider::names::NormalizedPropertyNameStr>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::zerovec::slice::ZeroSlice<icu_locid::subtags::region::Region>>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_locid_transform::provider::StrStrPairVarULE>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<icu_locid_transform::provider::LanguageStrStrPairVarULE>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::ule::unvalidated::UnvalidatedStr>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<str>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<str>>::as_slice Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<_, _>>::as_slice |
110 | | |
111 | | /// Try to allocate a buffer with enough capacity for `capacity` |
112 | | /// elements. Since `T` can take up an arbitrary size this will |
113 | | /// just allocate enough space for 4-byte Ts |
114 | 0 | pub(crate) fn with_capacity(capacity: usize) -> Self { |
115 | 0 | Self { |
116 | 0 | marker: PhantomData, |
117 | 0 | entire_slice: Vec::with_capacity(capacity * (F::INDEX_WIDTH + 4)), |
118 | 0 | } |
119 | 0 | } Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<zerovec::ule::unvalidated::UnvalidatedStr>>::with_capacity Unexecuted instantiation: <zerovec::varzerovec::owned::VarZeroVecOwned<_, _>>::with_capacity |
120 | | |
121 | | /// Try to reserve space for `capacity` |
122 | | /// elements. Since `T` can take up an arbitrary size this will |
123 | | /// just allocate enough space for 4-byte Ts |
124 | 0 | pub(crate) fn reserve(&mut self, capacity: usize) { |
125 | 0 | self.entire_slice.reserve(capacity * (F::INDEX_WIDTH + 4)) |
126 | 0 | } |
127 | | |
128 | | /// Get the position of a specific element in the data segment. |
129 | | /// |
130 | | /// If `idx == self.len()`, it will return the size of the data segment (where a new element would go). |
131 | | /// |
132 | | /// ## Safety |
133 | | /// `idx <= self.len()` and `self.as_encoded_bytes()` is well-formed. |
134 | 0 | unsafe fn element_position_unchecked(&self, idx: usize) -> usize { |
135 | 0 | let len = self.len(); |
136 | 0 | let out = if idx == len { |
137 | 0 | self.entire_slice.len() - LENGTH_WIDTH - METADATA_WIDTH - (F::INDEX_WIDTH * len) |
138 | | } else { |
139 | 0 | F::rawbytes_to_usize(*self.index_data(idx)) |
140 | | }; |
141 | 0 | debug_assert!( |
142 | 0 | out + LENGTH_WIDTH + METADATA_WIDTH + len * F::INDEX_WIDTH <= self.entire_slice.len() |
143 | | ); |
144 | 0 | out |
145 | 0 | } |
146 | | |
147 | | /// Get the range of a specific element in the data segment. |
148 | | /// |
149 | | /// ## Safety |
150 | | /// `idx < self.len()` and `self.as_encoded_bytes()` is well-formed. |
151 | 0 | unsafe fn element_range_unchecked(&self, idx: usize) -> core::ops::Range<usize> { |
152 | 0 | let start = self.element_position_unchecked(idx); |
153 | 0 | let end = self.element_position_unchecked(idx + 1); |
154 | 0 | debug_assert!(start <= end, "{start} > {end}"); |
155 | 0 | start..end |
156 | 0 | } |
157 | | |
158 | | /// Set the number of elements in the list without any checks. |
159 | | /// |
160 | | /// ## Safety |
161 | | /// No safe functions may be called until `self.as_encoded_bytes()` is well-formed. |
162 | 0 | unsafe fn set_len(&mut self, len: usize) { |
163 | 0 | assert!(len <= MAX_LENGTH); |
164 | 0 | let len_bytes = len.to_le_bytes(); |
165 | 0 | self.entire_slice[0..LENGTH_WIDTH].copy_from_slice(&len_bytes[0..LENGTH_WIDTH]); |
166 | 0 | // Double-check that the length fits in the length field |
167 | 0 | assert_eq!(len_bytes[LENGTH_WIDTH..].iter().sum::<u8>(), 0); |
168 | 0 | } |
169 | | |
170 | 0 | fn index_range(index: usize) -> Range<usize> { |
171 | 0 | let pos = LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * index; |
172 | 0 | pos..pos + F::INDEX_WIDTH |
173 | 0 | } |
174 | | |
175 | | /// Return the raw bytes representing the given `index`. |
176 | | /// |
177 | | /// ## Safety |
178 | | /// The index must be valid, and self.as_encoded_bytes() must be well-formed |
179 | 0 | unsafe fn index_data(&self, index: usize) -> &F::RawBytes { |
180 | 0 | &F::RawBytes::from_byte_slice_unchecked(&self.entire_slice[Self::index_range(index)])[0] |
181 | 0 | } |
182 | | |
183 | | /// Return the mutable slice representing the given `index`. |
184 | | /// |
185 | | /// ## Safety |
186 | | /// The index must be valid. self.as_encoded_bytes() must have allocated space |
187 | | /// for this index, but need not have its length appropriately set. |
188 | 0 | unsafe fn index_data_mut(&mut self, index: usize) -> &mut F::RawBytes { |
189 | 0 | let ptr = self.entire_slice.as_mut_ptr(); |
190 | 0 | let range = Self::index_range(index); |
191 | 0 |
|
192 | 0 | // Doing this instead of just `get_unchecked_mut()` because it's unclear |
193 | 0 | // if `get_unchecked_mut()` can be called out of bounds on a slice even |
194 | 0 | // if we know the buffer is larger. |
195 | 0 | let data = slice::from_raw_parts_mut(ptr.add(range.start), F::INDEX_WIDTH); |
196 | 0 |
|
197 | 0 | &mut F::rawbytes_from_byte_slice_unchecked_mut(data)[0] |
198 | 0 | } |
199 | | |
200 | | /// Shift the indices starting with and after `starting_index` by the provided `amount`. |
201 | | /// |
202 | | /// ## Safety |
203 | | /// Adding `amount` to each index after `starting_index` must not result in the slice from becoming malformed. |
204 | | /// The length of the slice must be correctly set. |
205 | 0 | unsafe fn shift_indices(&mut self, starting_index: usize, amount: i32) { |
206 | 0 | let len = self.len(); |
207 | 0 | let indices = F::rawbytes_from_byte_slice_unchecked_mut( |
208 | 0 | &mut self.entire_slice[LENGTH_WIDTH + METADATA_WIDTH |
209 | 0 | ..LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * len], |
210 | 0 | ); |
211 | 0 | for idx in &mut indices[starting_index..] { |
212 | 0 | let mut new_idx = F::rawbytes_to_usize(*idx); |
213 | 0 | if amount > 0 { |
214 | 0 | new_idx = new_idx.checked_add(amount.try_into().unwrap()).unwrap(); |
215 | 0 | } else { |
216 | 0 | new_idx = new_idx.checked_sub((-amount).try_into().unwrap()).unwrap(); |
217 | 0 | } |
218 | 0 | *idx = F::usize_to_rawbytes(new_idx); |
219 | | } |
220 | 0 | } |
221 | | |
222 | | /// Get this [`VarZeroVecOwned`] as a borrowed [`VarZeroVec`] |
223 | | /// |
224 | | /// If you wish to repeatedly call methods on this [`VarZeroVecOwned`], |
225 | | /// it is more efficient to perform this conversion first |
226 | 0 | pub fn as_varzerovec<'a>(&'a self) -> VarZeroVec<'a, T, F> { |
227 | 0 | self.as_slice().into() |
228 | 0 | } |
229 | | |
230 | | /// Empty the vector |
231 | 0 | pub fn clear(&mut self) { |
232 | 0 | self.entire_slice.clear() |
233 | 0 | } |
234 | | |
235 | | /// Consume this vector and return the backing buffer |
236 | | #[inline] |
237 | 0 | pub fn into_bytes(self) -> Vec<u8> { |
238 | 0 | self.entire_slice |
239 | 0 | } |
240 | | |
241 | | /// Invalidate and resize the data at an index, optionally inserting or removing the index. |
242 | | /// Also updates affected indices and the length. |
243 | | /// Returns a slice to the new element data - it doesn't contain uninitialized data but its value is indeterminate. |
244 | | /// |
245 | | /// ## Safety |
246 | | /// - `index` must be a valid index, or, if `shift_type == ShiftType::Insert`, `index == self.len()` is allowed. |
247 | | /// - `new_size` musn't result in the data segment growing larger than `F::MAX_VALUE`. |
248 | 0 | unsafe fn shift(&mut self, index: usize, new_size: usize, shift_type: ShiftType) -> &mut [u8] { |
249 | 0 | // The format of the encoded data is: |
250 | 0 | // - four bytes of "len" |
251 | 0 | // - len*4 bytes for an array of indices |
252 | 0 | // - the actual data to which the indices point |
253 | 0 | // |
254 | 0 | // When inserting or removing an element, the size of the indices segment must be changed, |
255 | 0 | // so the data before the target element must be shifted by 4 bytes in addition to the |
256 | 0 | // shifting needed for the new element size. |
257 | 0 | let len = self.len(); |
258 | 0 | let slice_len = self.entire_slice.len(); |
259 | | |
260 | 0 | let prev_element = match shift_type { |
261 | | ShiftType::Insert => { |
262 | 0 | let pos = self.element_position_unchecked(index); |
263 | 0 | // In the case of an insert, there's no previous element, |
264 | 0 | // so it's an empty range at the new position. |
265 | 0 | pos..pos |
266 | | } |
267 | 0 | _ => self.element_range_unchecked(index), |
268 | | }; |
269 | | |
270 | | // How much shifting must be done in bytes due to removal/insertion of an index. |
271 | 0 | let index_shift: i64 = match shift_type { |
272 | 0 | ShiftType::Insert => F::INDEX_WIDTH as i64, |
273 | 0 | ShiftType::Replace => 0, |
274 | 0 | ShiftType::Remove => -(F::INDEX_WIDTH as i64), |
275 | | }; |
276 | | // The total shift in byte size of the owned slice. |
277 | 0 | let shift: i64 = |
278 | 0 | new_size as i64 - (prev_element.end - prev_element.start) as i64 + index_shift; |
279 | 0 | let new_slice_len = slice_len.wrapping_add(shift as usize); |
280 | 0 | if shift > 0 { |
281 | 0 | if new_slice_len > F::MAX_VALUE as usize { |
282 | 0 | panic!( |
283 | 0 | "Attempted to grow VarZeroVec to an encoded size that does not fit within the length size used by {}", |
284 | 0 | any::type_name::<F>() |
285 | 0 | ); |
286 | 0 | } |
287 | 0 | self.entire_slice.resize(new_slice_len, 0); |
288 | 0 | } |
289 | | |
290 | | // Now that we've ensured there's enough space, we can shift the data around. |
291 | | { |
292 | | // Note: There are no references introduced between pointer creation and pointer use, and all |
293 | | // raw pointers are derived from a single &mut. This preserves pointer provenance. |
294 | 0 | let slice_range = self.entire_slice.as_mut_ptr_range(); |
295 | 0 | let old_slice_end = slice_range.start.add(slice_len); |
296 | 0 | let data_start = slice_range |
297 | 0 | .start |
298 | 0 | .add(LENGTH_WIDTH + METADATA_WIDTH + len * F::INDEX_WIDTH); |
299 | 0 | let prev_element_p = |
300 | 0 | data_start.add(prev_element.start)..data_start.add(prev_element.end); |
301 | 0 |
|
302 | 0 | // The memory range of the affected index. |
303 | 0 | // When inserting: where the new index goes. |
304 | 0 | // When removing: where the index being removed is. |
305 | 0 | // When replacing: unused. |
306 | 0 | let index_range = { |
307 | 0 | let index_start = slice_range |
308 | 0 | .start |
309 | 0 | .add(LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * index); |
310 | 0 | index_start..index_start.add(F::INDEX_WIDTH) |
311 | | }; |
312 | | |
313 | 0 | unsafe fn shift_bytes(block: Range<*const u8>, to: *mut u8) { |
314 | 0 | debug_assert!(block.end >= block.start); |
315 | 0 | ptr::copy(block.start, to, block.end.offset_from(block.start) as usize); |
316 | 0 | } |
317 | | |
318 | 0 | if shift_type == ShiftType::Remove { |
319 | 0 | // Move the data before the element back by 4 to remove the index. |
320 | 0 | shift_bytes(index_range.end..prev_element_p.start, index_range.start); |
321 | 0 | } |
322 | | |
323 | | // Shift data after the element to its new position. |
324 | 0 | shift_bytes( |
325 | 0 | prev_element_p.end..old_slice_end, |
326 | 0 | prev_element_p |
327 | 0 | .start |
328 | 0 | .offset((new_size as i64 + index_shift) as isize), |
329 | 0 | ); |
330 | | |
331 | 0 | let first_affected_index = match shift_type { |
332 | | ShiftType::Insert => { |
333 | | // Move data before the element forward by 4 to make space for a new index. |
334 | 0 | shift_bytes(index_range.start..prev_element_p.start, index_range.end); |
335 | 0 |
|
336 | 0 | *self.index_data_mut(index) = F::usize_to_rawbytes(prev_element.start); |
337 | 0 | self.set_len(len + 1); |
338 | 0 | index + 1 |
339 | | } |
340 | | ShiftType::Remove => { |
341 | 0 | self.set_len(len - 1); |
342 | 0 | index |
343 | | } |
344 | 0 | ShiftType::Replace => index + 1, |
345 | | }; |
346 | | // No raw pointer use should occur after this point (because of self.index_data and self.set_len). |
347 | | |
348 | | // Set the new slice length. This must be done after shifting data around to avoid uninitialized data. |
349 | 0 | self.entire_slice.set_len(new_slice_len); |
350 | 0 |
|
351 | 0 | // Shift the affected indices. |
352 | 0 | self.shift_indices(first_affected_index, (shift - index_shift) as i32); |
353 | 0 | }; |
354 | 0 |
|
355 | 0 | debug_assert!(self.verify_integrity()); |
356 | | |
357 | | // Return a mut slice to the new element data. |
358 | 0 | let element_pos = LENGTH_WIDTH |
359 | 0 | + METADATA_WIDTH |
360 | 0 | + self.len() * F::INDEX_WIDTH |
361 | 0 | + self.element_position_unchecked(index); |
362 | 0 | &mut self.entire_slice[element_pos..element_pos + new_size] |
363 | 0 | } |
364 | | |
365 | | /// Checks the internal invariants of the vec to ensure safe code will not cause UB. |
366 | | /// Returns whether integrity was verified. |
367 | | /// |
368 | | /// Note: an index is valid if it doesn't point to data past the end of the slice and is |
369 | | /// less than or equal to all future indices. The length of the index segment is not part of each index. |
370 | 0 | fn verify_integrity(&self) -> bool { |
371 | 0 | if self.is_empty() && !self.entire_slice.is_empty() { |
372 | 0 | return false; |
373 | 0 | } |
374 | 0 | let slice_len = self.entire_slice.len(); |
375 | 0 | match slice_len { |
376 | 0 | 0 => return true, |
377 | 0 | 1..=3 => return false, |
378 | 0 | _ => (), |
379 | 0 | } |
380 | 0 | let len = unsafe { |
381 | 0 | RawBytesULE::<LENGTH_WIDTH>::from_byte_slice_unchecked( |
382 | 0 | &self.entire_slice[..LENGTH_WIDTH], |
383 | 0 | )[0] |
384 | 0 | .as_unsigned_int() |
385 | 0 | }; |
386 | 0 | if len == 0 { |
387 | | // An empty vec must have an empty slice: there is only a single valid byte representation. |
388 | 0 | return false; |
389 | 0 | } |
390 | 0 | if slice_len < LENGTH_WIDTH + METADATA_WIDTH + len as usize * F::INDEX_WIDTH { |
391 | | // Not enough room for the indices. |
392 | 0 | return false; |
393 | 0 | } |
394 | 0 | let data_len = |
395 | 0 | self.entire_slice.len() - LENGTH_WIDTH - METADATA_WIDTH - len as usize * F::INDEX_WIDTH; |
396 | 0 | if data_len > MAX_INDEX { |
397 | | // The data segment is too long. |
398 | 0 | return false; |
399 | 0 | } |
400 | 0 |
|
401 | 0 | // Test index validity. |
402 | 0 | let indices = unsafe { |
403 | 0 | F::RawBytes::from_byte_slice_unchecked( |
404 | 0 | &self.entire_slice[LENGTH_WIDTH + METADATA_WIDTH |
405 | 0 | ..LENGTH_WIDTH + METADATA_WIDTH + len as usize * F::INDEX_WIDTH], |
406 | 0 | ) |
407 | | }; |
408 | 0 | for idx in indices { |
409 | 0 | if F::rawbytes_to_usize(*idx) > data_len { |
410 | | // Indices must not point past the data segment. |
411 | 0 | return false; |
412 | 0 | } |
413 | | } |
414 | 0 | for window in indices.windows(2) { |
415 | 0 | if F::rawbytes_to_usize(window[0]) > F::rawbytes_to_usize(window[1]) { |
416 | | // Indices must be in non-decreasing order. |
417 | 0 | return false; |
418 | 0 | } |
419 | | } |
420 | 0 | true |
421 | 0 | } |
422 | | |
423 | | /// Insert an element at the end of this vector |
424 | 0 | pub fn push<A: EncodeAsVarULE<T> + ?Sized>(&mut self, element: &A) { |
425 | 0 | self.insert(self.len(), element) |
426 | 0 | } |
427 | | |
428 | | /// Insert an element at index `idx` |
429 | 0 | pub fn insert<A: EncodeAsVarULE<T> + ?Sized>(&mut self, index: usize, element: &A) { |
430 | 0 | let len = self.len(); |
431 | 0 | if index > len { |
432 | 0 | panic!("Called out-of-bounds insert() on VarZeroVec, index {index} len {len}"); |
433 | 0 | } |
434 | 0 |
|
435 | 0 | let value_len = element.encode_var_ule_len(); |
436 | 0 |
|
437 | 0 | if len == 0 { |
438 | 0 | let header_len = LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH; |
439 | 0 | let cap = header_len + value_len; |
440 | 0 | self.entire_slice.resize(cap, 0); |
441 | 0 | self.entire_slice[0] = 1; // set length |
442 | 0 | element.encode_var_ule_write(&mut self.entire_slice[header_len..]); |
443 | 0 | return; |
444 | 0 | } |
445 | 0 |
|
446 | 0 | assert!(value_len < MAX_INDEX); |
447 | 0 | unsafe { |
448 | 0 | let place = self.shift(index, value_len, ShiftType::Insert); |
449 | 0 | element.encode_var_ule_write(place); |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | | /// Remove the element at index `idx` |
454 | 0 | pub fn remove(&mut self, index: usize) { |
455 | 0 | let len = self.len(); |
456 | 0 | if index >= len { |
457 | 0 | panic!("Called out-of-bounds remove() on VarZeroVec, index {index} len {len}"); |
458 | 0 | } |
459 | 0 | if len == 1 { |
460 | | // This is removing the last element. Set the slice to empty to ensure all empty vecs have empty data slices. |
461 | 0 | self.entire_slice.clear(); |
462 | 0 | return; |
463 | 0 | } |
464 | 0 | unsafe { |
465 | 0 | self.shift(index, 0, ShiftType::Remove); |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | /// Replace the element at index `idx` with another |
470 | 0 | pub fn replace<A: EncodeAsVarULE<T> + ?Sized>(&mut self, index: usize, element: &A) { |
471 | 0 | let len = self.len(); |
472 | 0 | if index >= len { |
473 | 0 | panic!("Called out-of-bounds replace() on VarZeroVec, index {index} len {len}"); |
474 | 0 | } |
475 | 0 |
|
476 | 0 | let value_len = element.encode_var_ule_len(); |
477 | 0 |
|
478 | 0 | assert!(value_len < MAX_INDEX); |
479 | 0 | unsafe { |
480 | 0 | let place = self.shift(index, value_len, ShiftType::Replace); |
481 | 0 | element.encode_var_ule_write(place); |
482 | 0 | } |
483 | 0 | } |
484 | | } |
485 | | |
486 | | impl<T: VarULE + ?Sized, F: VarZeroVecFormat> fmt::Debug for VarZeroVecOwned<T, F> |
487 | | where |
488 | | T: fmt::Debug, |
489 | | { |
490 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
491 | 0 | VarZeroSlice::fmt(self, f) |
492 | 0 | } |
493 | | } |
494 | | |
495 | | impl<T: VarULE + ?Sized, F> Default for VarZeroVecOwned<T, F> { |
496 | 0 | fn default() -> Self { |
497 | 0 | Self::new() |
498 | 0 | } |
499 | | } |
500 | | |
501 | | impl<T, A, F> PartialEq<&'_ [A]> for VarZeroVecOwned<T, F> |
502 | | where |
503 | | T: VarULE + ?Sized, |
504 | | T: PartialEq, |
505 | | A: AsRef<T>, |
506 | | F: VarZeroVecFormat, |
507 | | { |
508 | | #[inline] |
509 | 0 | fn eq(&self, other: &&[A]) -> bool { |
510 | 0 | self.iter().eq(other.iter().map(|t| t.as_ref())) |
511 | 0 | } |
512 | | } |
513 | | |
514 | | impl<'a, T: ?Sized + VarULE, F: VarZeroVecFormat> From<&'a VarZeroSlice<T, F>> |
515 | | for VarZeroVecOwned<T, F> |
516 | | { |
517 | 0 | fn from(other: &'a VarZeroSlice<T, F>) -> Self { |
518 | 0 | Self::from_slice(other) |
519 | 0 | } |
520 | | } |
521 | | |
522 | | #[cfg(test)] |
523 | | mod test { |
524 | | use super::VarZeroVecOwned; |
525 | | #[test] |
526 | | fn test_insert_integrity() { |
527 | | let mut items: Vec<String> = Vec::new(); |
528 | | let mut zerovec = VarZeroVecOwned::<str>::new(); |
529 | | |
530 | | // Insert into an empty vec. |
531 | | items.insert(0, "1234567890".into()); |
532 | | zerovec.insert(0, "1234567890"); |
533 | | assert_eq!(zerovec, &*items); |
534 | | |
535 | | zerovec.insert(1, "foo3"); |
536 | | items.insert(1, "foo3".into()); |
537 | | assert_eq!(zerovec, &*items); |
538 | | |
539 | | // Insert at the end. |
540 | | items.insert(items.len(), "qwertyuiop".into()); |
541 | | zerovec.insert(zerovec.len(), "qwertyuiop"); |
542 | | assert_eq!(zerovec, &*items); |
543 | | |
544 | | items.insert(0, "asdfghjkl;".into()); |
545 | | zerovec.insert(0, "asdfghjkl;"); |
546 | | assert_eq!(zerovec, &*items); |
547 | | |
548 | | items.insert(2, "".into()); |
549 | | zerovec.insert(2, ""); |
550 | | assert_eq!(zerovec, &*items); |
551 | | } |
552 | | |
553 | | #[test] |
554 | | // ensure that inserting empty items works |
555 | | fn test_empty_inserts() { |
556 | | let mut items: Vec<String> = Vec::new(); |
557 | | let mut zerovec = VarZeroVecOwned::<str>::new(); |
558 | | |
559 | | // Insert into an empty vec. |
560 | | items.insert(0, "".into()); |
561 | | zerovec.insert(0, ""); |
562 | | assert_eq!(zerovec, &*items); |
563 | | |
564 | | items.insert(0, "".into()); |
565 | | zerovec.insert(0, ""); |
566 | | assert_eq!(zerovec, &*items); |
567 | | |
568 | | items.insert(0, "1234567890".into()); |
569 | | zerovec.insert(0, "1234567890"); |
570 | | assert_eq!(zerovec, &*items); |
571 | | |
572 | | items.insert(0, "".into()); |
573 | | zerovec.insert(0, ""); |
574 | | assert_eq!(zerovec, &*items); |
575 | | } |
576 | | |
577 | | #[test] |
578 | | fn test_small_insert_integrity() { |
579 | | // Tests that insert() works even when there |
580 | | // is not enough space for the new index in entire_slice.len() |
581 | | let mut items: Vec<String> = Vec::new(); |
582 | | let mut zerovec = VarZeroVecOwned::<str>::new(); |
583 | | |
584 | | // Insert into an empty vec. |
585 | | items.insert(0, "abc".into()); |
586 | | zerovec.insert(0, "abc"); |
587 | | assert_eq!(zerovec, &*items); |
588 | | |
589 | | zerovec.insert(1, "def"); |
590 | | items.insert(1, "def".into()); |
591 | | assert_eq!(zerovec, &*items); |
592 | | } |
593 | | |
594 | | #[test] |
595 | | #[should_panic] |
596 | | fn test_insert_past_end() { |
597 | | VarZeroVecOwned::<str>::new().insert(1, ""); |
598 | | } |
599 | | |
600 | | #[test] |
601 | | fn test_remove_integrity() { |
602 | | let mut items: Vec<&str> = vec!["apples", "bananas", "eeples", "", "baneenees", "five", ""]; |
603 | | let mut zerovec = VarZeroVecOwned::<str>::try_from_elements(&items).unwrap(); |
604 | | |
605 | | for index in [0, 2, 4, 0, 1, 1, 0] { |
606 | | items.remove(index); |
607 | | zerovec.remove(index); |
608 | | assert_eq!(zerovec, &*items, "index {}, len {}", index, items.len()); |
609 | | } |
610 | | } |
611 | | |
612 | | #[test] |
613 | | fn test_removing_last_element_clears() { |
614 | | let mut zerovec = VarZeroVecOwned::<str>::try_from_elements(&["buy some apples"]).unwrap(); |
615 | | assert!(!zerovec.as_bytes().is_empty()); |
616 | | zerovec.remove(0); |
617 | | assert!(zerovec.as_bytes().is_empty()); |
618 | | } |
619 | | |
620 | | #[test] |
621 | | #[should_panic] |
622 | | fn test_remove_past_end() { |
623 | | VarZeroVecOwned::<str>::new().remove(0); |
624 | | } |
625 | | |
626 | | #[test] |
627 | | fn test_replace_integrity() { |
628 | | let mut items: Vec<&str> = vec!["apples", "bananas", "eeples", "", "baneenees", "five", ""]; |
629 | | let mut zerovec = VarZeroVecOwned::<str>::try_from_elements(&items).unwrap(); |
630 | | |
631 | | // Replace with an element of the same size (and the first element) |
632 | | items[0] = "blablah"; |
633 | | zerovec.replace(0, "blablah"); |
634 | | assert_eq!(zerovec, &*items); |
635 | | |
636 | | // Replace with a smaller element |
637 | | items[1] = "twily"; |
638 | | zerovec.replace(1, "twily"); |
639 | | assert_eq!(zerovec, &*items); |
640 | | |
641 | | // Replace an empty element |
642 | | items[3] = "aoeuidhtns"; |
643 | | zerovec.replace(3, "aoeuidhtns"); |
644 | | assert_eq!(zerovec, &*items); |
645 | | |
646 | | // Replace the last element |
647 | | items[6] = "0123456789"; |
648 | | zerovec.replace(6, "0123456789"); |
649 | | assert_eq!(zerovec, &*items); |
650 | | |
651 | | // Replace with an empty element |
652 | | items[2] = ""; |
653 | | zerovec.replace(2, ""); |
654 | | assert_eq!(zerovec, &*items); |
655 | | } |
656 | | |
657 | | #[test] |
658 | | #[should_panic] |
659 | | fn test_replace_past_end() { |
660 | | VarZeroVecOwned::<str>::new().replace(0, ""); |
661 | | } |
662 | | } |