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 | use crate::{ZeroMap2d, ZeroSlice}; |
6 | |
7 | use core::cmp::Ordering; |
8 | use core::fmt; |
9 | use core::ops::Range; |
10 | |
11 | use crate::map::ZeroMapKV; |
12 | use crate::map::ZeroVecLike; |
13 | |
14 | use super::ZeroMap2dBorrowed; |
15 | |
16 | /// An intermediate state of queries over [`ZeroMap2d`] and [`ZeroMap2dBorrowed`]. |
17 | pub struct ZeroMap2dCursor<'l, 'a, K0, K1, V> |
18 | where |
19 | K0: ZeroMapKV<'a>, |
20 | K1: ZeroMapKV<'a>, |
21 | V: ZeroMapKV<'a>, |
22 | K0: ?Sized, |
23 | K1: ?Sized, |
24 | V: ?Sized, |
25 | { |
26 | // Invariant: these fields have the same invariants as they do in ZeroMap2d |
27 | keys0: &'l K0::Slice, |
28 | joiner: &'l ZeroSlice<u32>, |
29 | keys1: &'l K1::Slice, |
30 | values: &'l V::Slice, |
31 | // Invariant: key0_index is in range |
32 | key0_index: usize, |
33 | } |
34 | |
35 | impl<'a, K0, K1, V> ZeroMap2dCursor<'a, 'a, K0, K1, V> |
36 | where |
37 | K0: ZeroMapKV<'a>, |
38 | K1: ZeroMapKV<'a>, |
39 | V: ZeroMapKV<'a>, |
40 | K0: ?Sized, |
41 | K1: ?Sized, |
42 | V: ?Sized, |
43 | { |
44 | /// `key0_index` must be in range |
45 | pub(crate) fn from_borrowed( |
46 | borrowed: &ZeroMap2dBorrowed<'a, K0, K1, V>, |
47 | key0_index: usize, |
48 | ) -> Self { |
49 | debug_assert!(key0_index < borrowed.joiner.len()); |
50 | ZeroMap2dCursor { |
51 | keys0: borrowed.keys0, |
52 | joiner: borrowed.joiner, |
53 | keys1: borrowed.keys1, |
54 | values: borrowed.values, |
55 | key0_index, |
56 | } |
57 | } |
58 | } |
59 | |
60 | impl<'l, 'a, K0, K1, V> ZeroMap2dCursor<'l, 'a, K0, K1, V> |
61 | where |
62 | K0: ZeroMapKV<'a>, |
63 | K1: ZeroMapKV<'a>, |
64 | V: ZeroMapKV<'a>, |
65 | K0: ?Sized, |
66 | K1: ?Sized, |
67 | V: ?Sized, |
68 | { |
69 | /// `key0_index` must be in range |
70 | pub(crate) fn from_cow(cow: &'l ZeroMap2d<'a, K0, K1, V>, key0_index: usize) -> Self { |
71 | debug_assert!(key0_index < cow.joiner.len()); |
72 | Self { |
73 | keys0: cow.keys0.zvl_as_borrowed(), |
74 | joiner: &cow.joiner, |
75 | keys1: cow.keys1.zvl_as_borrowed(), |
76 | values: cow.values.zvl_as_borrowed(), |
77 | key0_index, |
78 | } |
79 | } |
80 | |
81 | /// Returns the key0 corresponding to the cursor position. |
82 | /// |
83 | /// ```rust |
84 | /// use zerovec::ZeroMap2d; |
85 | /// |
86 | /// let mut map = ZeroMap2d::new(); |
87 | /// map.insert("one" , &1u32, "foo" ); |
88 | /// assert_eq!(map.get0("one" ).unwrap().key0(), "one" ); |
89 | /// ``` |
90 | pub fn key0(&self) -> &'l K0::GetType { |
91 | #[allow (clippy::unwrap_used)] // safe by invariant on `self.key0_index` |
92 | self.keys0.zvl_get(self.key0_index).unwrap() |
93 | } |
94 | |
95 | /// Borrow an ordered iterator over keys1 and values for a particular key0. |
96 | /// |
97 | /// To get the values as copy types, see [`Self::iter1_copied`]. |
98 | /// |
99 | /// For an example, see [`ZeroMap2d::iter0()`]. |
100 | pub fn iter1( |
101 | &self, |
102 | ) -> impl Iterator< |
103 | Item = ( |
104 | &'l <K1 as ZeroMapKV<'a>>::GetType, |
105 | &'l <V as ZeroMapKV<'a>>::GetType, |
106 | ), |
107 | > + '_ { |
108 | let range = self.get_range(); |
109 | #[allow (clippy::unwrap_used)] // `self.get_range()` returns a valid range |
110 | range.map(move |idx| { |
111 | ( |
112 | self.keys1.zvl_get(idx).unwrap(), |
113 | self.values.zvl_get(idx).unwrap(), |
114 | ) |
115 | }) |
116 | } |
117 | |
118 | /// Transform this cursor into an ordered iterator over keys1 for a particular key0. |
119 | pub fn into_iter1( |
120 | self, |
121 | ) -> impl Iterator< |
122 | Item = ( |
123 | &'l <K1 as ZeroMapKV<'a>>::GetType, |
124 | &'l <V as ZeroMapKV<'a>>::GetType, |
125 | ), |
126 | > { |
127 | let range = self.get_range(); |
128 | #[allow (clippy::unwrap_used)] // `self.get_range()` returns a valid range |
129 | range.map(move |idx| { |
130 | ( |
131 | self.keys1.zvl_get(idx).unwrap(), |
132 | self.values.zvl_get(idx).unwrap(), |
133 | ) |
134 | }) |
135 | } |
136 | |
137 | /// Given key0_index, returns the corresponding range of keys1, which will be valid |
138 | pub(super) fn get_range(&self) -> Range<usize> { |
139 | debug_assert!(self.key0_index < self.joiner.len()); |
140 | let start = if self.key0_index == 0 { |
141 | 0 |
142 | } else { |
143 | #[allow (clippy::unwrap_used)] // protected by the debug_assert above |
144 | self.joiner.get(self.key0_index - 1).unwrap() |
145 | }; |
146 | #[allow (clippy::unwrap_used)] // protected by the debug_assert above |
147 | let limit = self.joiner.get(self.key0_index).unwrap(); |
148 | // These two assertions are true based on the invariants of ZeroMap2d |
149 | debug_assert!(start < limit); |
150 | debug_assert!((limit as usize) <= self.values.zvl_len()); |
151 | (start as usize)..(limit as usize) |
152 | } |
153 | } |
154 | |
155 | impl<'l, 'a, K0, K1, V> ZeroMap2dCursor<'l, 'a, K0, K1, V> |
156 | where |
157 | K0: ZeroMapKV<'a>, |
158 | K1: ZeroMapKV<'a>, |
159 | V: ZeroMapKV<'a>, |
160 | K0: ?Sized, |
161 | K1: ?Sized, |
162 | V: Copy, |
163 | { |
164 | /// Borrow an ordered iterator over keys1 and values for a particular key0. |
165 | /// |
166 | /// The values are returned as copy types. |
167 | /// |
168 | /// # Examples |
169 | /// |
170 | /// ``` |
171 | /// use zerovec::ZeroMap2d; |
172 | /// |
173 | /// let zm2d: ZeroMap2d<str, u8, usize> = [ |
174 | /// ("a" , 0u8, 1usize), |
175 | /// ("b" , 1u8, 1000usize), |
176 | /// ("b" , 2u8, 2000usize), |
177 | /// ] |
178 | /// .into_iter() |
179 | /// .collect(); |
180 | /// |
181 | /// let mut total_value = 0; |
182 | /// |
183 | /// for cursor in zm2d.iter0() { |
184 | /// for (_, value) in cursor.iter1_copied() { |
185 | /// total_value += value; |
186 | /// } |
187 | /// } |
188 | /// |
189 | /// assert_eq!(total_value, 3001); |
190 | /// ``` |
191 | pub fn iter1_copied( |
192 | &self, |
193 | ) -> impl Iterator<Item = (&'l <K1 as ZeroMapKV<'a>>::GetType, V)> + '_ { |
194 | let range = self.get_range(); |
195 | #[allow (clippy::unwrap_used)] // `self.get_range()` returns a valid range |
196 | range.map(move |idx| { |
197 | ( |
198 | self.keys1.zvl_get(idx).unwrap(), |
199 | self.get1_copied_at(idx).unwrap(), |
200 | ) |
201 | }) |
202 | } |
203 | |
204 | fn get1_copied_at(&self, index: usize) -> Option<V> { |
205 | let ule = self.values.zvl_get(index)?; |
206 | let mut result = Option::<V>::None; |
207 | V::Container::zvl_get_as_t(ule, |v| result.replace(*v)); |
208 | #[allow (clippy::unwrap_used)] // `zvl_get_as_t` guarantees that the callback is invoked |
209 | Some(result.unwrap()) |
210 | } |
211 | } |
212 | |
213 | impl<'l, 'a, K0, K1, V> ZeroMap2dCursor<'l, 'a, K0, K1, V> |
214 | where |
215 | K0: ZeroMapKV<'a>, |
216 | K1: ZeroMapKV<'a> + Ord, |
217 | V: ZeroMapKV<'a>, |
218 | K0: ?Sized, |
219 | K1: ?Sized, |
220 | V: ?Sized, |
221 | { |
222 | /// Gets the value for a key1 from this cursor, or `None` if key1 is not in the map. |
223 | /// |
224 | /// ```rust |
225 | /// use zerovec::ZeroMap2d; |
226 | /// |
227 | /// let mut map = ZeroMap2d::new(); |
228 | /// map.insert("one" , &1u32, "foo" ); |
229 | /// assert_eq!(map.get0("one" ).unwrap().get1(&1), Some("foo" )); |
230 | /// assert_eq!(map.get0("one" ).unwrap().get1(&2), None); |
231 | /// ``` |
232 | pub fn get1(&self, key1: &K1) -> Option<&'l V::GetType> { |
233 | let key1_index = self.get_key1_index(key1)?; |
234 | #[allow (clippy::unwrap_used)] // key1_index is valid |
235 | Some(self.values.zvl_get(key1_index).unwrap()) |
236 | } |
237 | |
238 | /// Gets the value for a predicate from this cursor, or `None` if key1 is not in the map. |
239 | /// |
240 | /// ```rust |
241 | /// use zerovec::ZeroMap2d; |
242 | /// |
243 | /// let mut map = ZeroMap2d::new(); |
244 | /// map.insert("one" , &1u32, "foo" ); |
245 | /// assert_eq!(map.get0("one" ).unwrap().get1_by(|v| v.cmp(&1)), Some("foo" )); |
246 | /// assert_eq!(map.get0("one" ).unwrap().get1_by(|v| v.cmp(&2)), None); |
247 | /// ``` |
248 | pub fn get1_by(&self, predicate: impl FnMut(&K1) -> Ordering) -> Option<&'l V::GetType> { |
249 | let key1_index = self.get_key1_index_by(predicate)?; |
250 | #[allow (clippy::unwrap_used)] // key1_index is valid |
251 | Some(self.values.zvl_get(key1_index).unwrap()) |
252 | } |
253 | |
254 | /// Given key0_index and predicate, returns the index into the values array |
255 | fn get_key1_index_by(&self, predicate: impl FnMut(&K1) -> Ordering) -> Option<usize> { |
256 | let range = self.get_range(); |
257 | debug_assert!(range.start < range.end); // '<' because every key0 should have a key1 |
258 | debug_assert!(range.end <= self.keys1.zvl_len()); |
259 | let start = range.start; |
260 | #[allow (clippy::expect_used)] // protected by the debug_assert above |
261 | let binary_search_result = self |
262 | .keys1 |
263 | .zvl_binary_search_in_range_by(predicate, range) |
264 | .expect("in-bounds range" ); |
265 | binary_search_result.ok().map(move |s| s + start) |
266 | } |
267 | |
268 | /// Given key0_index and key1, returns the index into the values array |
269 | fn get_key1_index(&self, key1: &K1) -> Option<usize> { |
270 | let range = self.get_range(); |
271 | debug_assert!(range.start < range.end); // '<' because every key0 should have a key1 |
272 | debug_assert!(range.end <= self.keys1.zvl_len()); |
273 | let start = range.start; |
274 | #[allow (clippy::expect_used)] // protected by the debug_assert above |
275 | let binary_search_result = self |
276 | .keys1 |
277 | .zvl_binary_search_in_range(key1, range) |
278 | .expect("in-bounds range" ); |
279 | binary_search_result.ok().map(move |s| s + start) |
280 | } |
281 | } |
282 | |
283 | impl<'l, 'a, K0, K1, V> ZeroMap2dCursor<'l, 'a, K0, K1, V> |
284 | where |
285 | K0: ZeroMapKV<'a>, |
286 | K1: ZeroMapKV<'a> + Ord, |
287 | V: ZeroMapKV<'a>, |
288 | V: Copy, |
289 | K0: ?Sized, |
290 | K1: ?Sized, |
291 | { |
292 | /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE` |
293 | /// |
294 | /// ```rust |
295 | /// use zerovec::ZeroMap2d; |
296 | /// |
297 | /// let mut map: ZeroMap2d<u16, u16, u16> = ZeroMap2d::new(); |
298 | /// map.insert(&1, &2, &3); |
299 | /// map.insert(&1, &4, &5); |
300 | /// map.insert(&6, &7, &8); |
301 | /// |
302 | /// assert_eq!(map.get0(&6).unwrap().get1_copied(&7), Some(8)); |
303 | /// ``` |
304 | #[inline ] |
305 | pub fn get1_copied(&self, key1: &K1) -> Option<V> { |
306 | let key1_index = self.get_key1_index(key1)?; |
307 | self.get1_copied_at(key1_index) |
308 | } |
309 | |
310 | /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE` |
311 | #[inline ] |
312 | pub fn get1_copied_by(&self, predicate: impl FnMut(&K1) -> Ordering) -> Option<V> { |
313 | let key1_index = self.get_key1_index_by(predicate)?; |
314 | self.get1_copied_at(key1_index) |
315 | } |
316 | } |
317 | |
318 | // We can't use the default PartialEq because ZeroMap2d is invariant |
319 | // so otherwise rustc will not automatically allow you to compare ZeroMaps |
320 | // with different lifetimes |
321 | impl<'m, 'n, 'a, 'b, K0, K1, V> PartialEq<ZeroMap2dCursor<'n, 'b, K0, K1, V>> |
322 | for ZeroMap2dCursor<'m, 'a, K0, K1, V> |
323 | where |
324 | K0: for<'c> ZeroMapKV<'c> + ?Sized, |
325 | K1: for<'c> ZeroMapKV<'c> + ?Sized, |
326 | V: for<'c> ZeroMapKV<'c> + ?Sized, |
327 | <K0 as ZeroMapKV<'a>>::Slice: PartialEq<<K0 as ZeroMapKV<'b>>::Slice>, |
328 | <K1 as ZeroMapKV<'a>>::Slice: PartialEq<<K1 as ZeroMapKV<'b>>::Slice>, |
329 | <V as ZeroMapKV<'a>>::Slice: PartialEq<<V as ZeroMapKV<'b>>::Slice>, |
330 | { |
331 | fn eq(&self, other: &ZeroMap2dCursor<'n, 'b, K0, K1, V>) -> bool { |
332 | self.keys0.eq(other.keys0) |
333 | && self.joiner.eq(other.joiner) |
334 | && self.keys1.eq(other.keys1) |
335 | && self.values.eq(other.values) |
336 | && self.key0_index.eq(&other.key0_index) |
337 | } |
338 | } |
339 | |
340 | impl<'l, 'a, K0, K1, V> fmt::Debug for ZeroMap2dCursor<'l, 'a, K0, K1, V> |
341 | where |
342 | K0: ZeroMapKV<'a> + ?Sized, |
343 | K1: ZeroMapKV<'a> + ?Sized, |
344 | V: ZeroMapKV<'a> + ?Sized, |
345 | K0::Slice: fmt::Debug, |
346 | K1::Slice: fmt::Debug, |
347 | V::Slice: fmt::Debug, |
348 | { |
349 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
350 | f&mut DebugStruct<'_, '_>.debug_struct("ZeroMap2d" ) |
351 | .field("keys0" , &self.keys0) |
352 | .field("joiner" , &self.joiner) |
353 | .field("keys1" , &self.keys1) |
354 | .field("values" , &self.values) |
355 | .field(name:"key0_index" , &self.key0_index) |
356 | .finish() |
357 | } |
358 | } |
359 | |