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
5use crate::ZeroSlice;
6
7use core::cmp::Ordering;
8use core::fmt;
9
10use crate::map::ZeroMapKV;
11use crate::map::ZeroVecLike;
12use crate::map2d::ZeroMap2dCursor;
13
14/// A borrowed-only version of [`ZeroMap2d`](super::ZeroMap2d)
15///
16/// This is useful for fully-zero-copy deserialization from non-human-readable
17/// serialization formats. It also has the advantage that it can return references that live for
18/// the lifetime of the backing buffer as opposed to that of the [`ZeroMap2dBorrowed`] instance.
19///
20/// # Examples
21///
22/// ```
23/// use zerovec::maps::ZeroMap2dBorrowed;
24///
25/// // Example byte buffer representing the map { 1: {2: "three" } }
26/// let BINCODE_BYTES: &[u8; 51] = &[
27/// 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0,
28/// 0, 0, 0, 0, 0, 0, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 116,
29/// 104, 114, 101, 101,
30/// ];
31///
32/// // Deserializing to ZeroMap2d requires no heap allocations.
33/// let zero_map: ZeroMap2dBorrowed<u16, u16, str> =
34/// bincode::deserialize(BINCODE_BYTES)
35/// .expect("Should deserialize successfully");
36/// assert_eq!(zero_map.get_2d(&1, &2), Some("three"));
37/// ```
38///
39/// This can be obtained from a [`ZeroMap2d`](super::ZeroMap2d) via [`ZeroMap2d::as_borrowed`](super::ZeroMap2d::as_borrowed)
40pub struct ZeroMap2dBorrowed<'a, K0, K1, V>
41where
42 K0: ZeroMapKV<'a>,
43 K1: ZeroMapKV<'a>,
44 V: ZeroMapKV<'a>,
45 K0: ?Sized,
46 K1: ?Sized,
47 V: ?Sized,
48{
49 pub(crate) keys0: &'a K0::Slice,
50 pub(crate) joiner: &'a ZeroSlice<u32>,
51 pub(crate) keys1: &'a K1::Slice,
52 pub(crate) values: &'a V::Slice,
53}
54
55impl<'a, K0, K1, V> Copy for ZeroMap2dBorrowed<'a, K0, K1, V>
56where
57 K0: ZeroMapKV<'a>,
58 K1: ZeroMapKV<'a>,
59 V: ZeroMapKV<'a>,
60 K0: ?Sized,
61 K1: ?Sized,
62 V: ?Sized,
63{
64}
65
66impl<'a, K0, K1, V> Clone for ZeroMap2dBorrowed<'a, K0, K1, V>
67where
68 K0: ZeroMapKV<'a>,
69 K1: ZeroMapKV<'a>,
70 V: ZeroMapKV<'a>,
71 K0: ?Sized,
72 K1: ?Sized,
73 V: ?Sized,
74{
75 fn clone(&self) -> Self {
76 *self
77 }
78}
79
80impl<'a, K0, K1, V> Default for ZeroMap2dBorrowed<'a, K0, K1, V>
81where
82 K0: ZeroMapKV<'a>,
83 K1: ZeroMapKV<'a>,
84 V: ZeroMapKV<'a>,
85 K0::Slice: 'static,
86 K1::Slice: 'static,
87 V::Slice: 'static,
88 K0: ?Sized,
89 K1: ?Sized,
90 V: ?Sized,
91{
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97impl<'a, K0, K1, V> ZeroMap2dBorrowed<'a, K0, K1, V>
98where
99 K0: ZeroMapKV<'a>,
100 K1: ZeroMapKV<'a>,
101 V: ZeroMapKV<'a>,
102 K0::Slice: 'static,
103 K1::Slice: 'static,
104 V::Slice: 'static,
105 K0: ?Sized,
106 K1: ?Sized,
107 V: ?Sized,
108{
109 /// Creates a new, empty `ZeroMap2dBorrowed<K0, K1, V>`.
110 ///
111 /// Note: Since [`ZeroMap2dBorrowed`] is not mutable, the return value will be a stub unless
112 /// converted into a [`ZeroMap2d`](super::ZeroMap2d).
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use zerovec::maps::ZeroMap2dBorrowed;
118 ///
119 /// let zm: ZeroMap2dBorrowed<u16, u16, str> = ZeroMap2dBorrowed::new();
120 /// assert!(zm.is_empty());
121 /// ```
122 pub fn new() -> Self {
123 Self {
124 keys0: K0::Container::zvl_new_borrowed(),
125 joiner: Default::default(),
126 keys1: K1::Container::zvl_new_borrowed(),
127 values: V::Container::zvl_new_borrowed(),
128 }
129 }
130}
131
132impl<'a, K0, K1, V> ZeroMap2dBorrowed<'a, K0, K1, V>
133where
134 K0: ZeroMapKV<'a>,
135 K1: ZeroMapKV<'a>,
136 V: ZeroMapKV<'a>,
137 K0: ?Sized,
138 K1: ?Sized,
139 V: ?Sized,
140{
141 #[doc(hidden)] // databake internal
142 pub const unsafe fn from_parts_unchecked(
143 keys0: &'a K0::Slice,
144 joiner: &'a ZeroSlice<u32>,
145 keys1: &'a K1::Slice,
146 values: &'a V::Slice,
147 ) -> Self {
148 Self {
149 keys0,
150 joiner,
151 keys1,
152 values,
153 }
154 }
155
156 /// The number of elements in the [`ZeroMap2dBorrowed`]
157 pub fn len(&self) -> usize {
158 self.values.zvl_len()
159 }
160
161 /// Whether the [`ZeroMap2dBorrowed`] is empty
162 pub fn is_empty(&self) -> bool {
163 self.values.zvl_len() == 0
164 }
165}
166
167impl<'a, K0, K1, V> ZeroMap2dBorrowed<'a, K0, K1, V>
168where
169 K0: ZeroMapKV<'a> + Ord,
170 K1: ZeroMapKV<'a> + Ord,
171 V: ZeroMapKV<'a>,
172 K0: ?Sized,
173 K1: ?Sized,
174 V: ?Sized,
175{
176 /// Get the value associated with `key0` and `key1`, if it exists.
177 ///
178 /// This is able to return values that live longer than the map itself
179 /// since they borrow directly from the backing buffer. This is the
180 /// primary advantage of using [`ZeroMap2dBorrowed`](super::ZeroMap2dBorrowed) over [`ZeroMap2d`](super::ZeroMap2d).
181 ///
182 /// ```rust
183 /// use zerovec::maps::ZeroMap2dBorrowed;
184 /// use zerovec::ZeroMap2d;
185 ///
186 /// let mut map = ZeroMap2d::new();
187 /// map.insert(&1, "one", "foo");
188 /// map.insert(&2, "one", "bar");
189 /// map.insert(&2, "two", "baz");
190 ///
191 /// let borrowed = map.as_borrowed();
192 /// assert_eq!(borrowed.get_2d(&1, "one"), Some("foo"));
193 /// assert_eq!(borrowed.get_2d(&1, "two"), None);
194 /// assert_eq!(borrowed.get_2d(&2, "one"), Some("bar"));
195 /// assert_eq!(borrowed.get_2d(&2, "two"), Some("baz"));
196 /// assert_eq!(borrowed.get_2d(&3, "three"), None);
197 /// ```
198 pub fn get_2d(&self, key0: &K0, key1: &K1) -> Option<&'a V::GetType> {
199 self.get0(key0)?.get1(key1)
200 }
201}
202
203impl<'a, K0, K1, V> ZeroMap2dBorrowed<'a, K0, K1, V>
204where
205 K0: ZeroMapKV<'a> + Ord,
206 K1: ZeroMapKV<'a>,
207 V: ZeroMapKV<'a>,
208 K0: ?Sized,
209 K1: ?Sized,
210 V: ?Sized,
211{
212 /// Gets a cursor for `key0`. If `None`, then `key0` is not in the map. If `Some`,
213 /// then `key0` is in the map, and `key1` can be queried.
214 ///
215 /// ```rust
216 /// use zerovec::maps::ZeroMap2dBorrowed;
217 /// use zerovec::ZeroMap2d;
218 ///
219 /// let mut map = ZeroMap2d::new();
220 /// map.insert(&1, "one", "foo");
221 /// map.insert(&2, "two", "bar");
222 /// let borrowed = map.as_borrowed();
223 /// assert!(matches!(borrowed.get0(&1), Some(_)));
224 /// assert!(matches!(borrowed.get0(&3), None));
225 /// ```
226 #[inline]
227 pub fn get0<'l>(&'l self, key0: &K0) -> Option<ZeroMap2dCursor<'a, 'a, K0, K1, V>> {
228 let key0_index = self.keys0.zvl_binary_search(key0).ok()?;
229 Some(ZeroMap2dCursor::from_borrowed(self, key0_index))
230 }
231
232 /// Binary search the map for `key0`, returning a cursor.
233 ///
234 /// ```rust
235 /// use zerovec::maps::ZeroMap2dBorrowed;
236 /// use zerovec::ZeroMap2d;
237 ///
238 /// let mut map = ZeroMap2d::new();
239 /// map.insert(&1, "one", "foo");
240 /// map.insert(&2, "two", "bar");
241 /// let borrowed = map.as_borrowed();
242 /// assert!(matches!(borrowed.get0_by(|probe| probe.cmp(&1)), Some(_)));
243 /// assert!(matches!(borrowed.get0_by(|probe| probe.cmp(&3)), None));
244 /// ```
245 pub fn get0_by<'l>(
246 &'l self,
247 predicate: impl FnMut(&K0) -> Ordering,
248 ) -> Option<ZeroMap2dCursor<'a, 'a, K0, K1, V>> {
249 let key0_index = self.keys0.zvl_binary_search_by(predicate).ok()?;
250 Some(ZeroMap2dCursor::from_borrowed(self, key0_index))
251 }
252
253 /// Returns whether `key0` is contained in this map
254 ///
255 /// ```rust
256 /// use zerovec::maps::ZeroMap2dBorrowed;
257 /// use zerovec::ZeroMap2d;
258 ///
259 /// let mut map = ZeroMap2d::new();
260 /// map.insert(&1, "one", "foo");
261 /// map.insert(&2, "two", "bar");
262 /// let borrowed = map.as_borrowed();
263 /// assert!(borrowed.contains_key0(&1));
264 /// assert!(!borrowed.contains_key0(&3));
265 /// ```
266 pub fn contains_key0(&self, key0: &K0) -> bool {
267 self.keys0.zvl_binary_search(key0).is_ok()
268 }
269}
270
271impl<'a, K0, K1, V> ZeroMap2dBorrowed<'a, K0, K1, V>
272where
273 K0: ZeroMapKV<'a>,
274 K1: ZeroMapKV<'a>,
275 V: ZeroMapKV<'a>,
276 K0: ?Sized,
277 K1: ?Sized,
278 V: ?Sized,
279{
280 /// Produce an ordered iterator over keys0
281 pub fn iter0<'l>(&'l self) -> impl Iterator<Item = ZeroMap2dCursor<'a, 'a, K0, K1, V>> + '_ {
282 (0..self.keys0.zvl_len()).map(move |idx: usize| ZeroMap2dCursor::from_borrowed(self, key0_index:idx))
283 }
284}
285
286impl<'a, K0, K1, V> ZeroMap2dBorrowed<'a, K0, K1, V>
287where
288 K0: ZeroMapKV<'a> + Ord,
289 K1: ZeroMapKV<'a> + Ord,
290 V: ZeroMapKV<'a>,
291 V: Copy,
292 K0: ?Sized,
293 K1: ?Sized,
294{
295 /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE`
296 pub fn get_copied_2d(&self, key0: &K0, key1: &K1) -> Option<V> {
297 self.get0(key0)?.get1_copied(key1)
298 }
299}
300
301// We can't use the default PartialEq because ZeroMap2d is invariant
302// so otherwise rustc will not automatically allow you to compare ZeroMaps
303// with different lifetimes
304impl<'a, 'b, K0, K1, V> PartialEq<ZeroMap2dBorrowed<'b, K0, K1, V>>
305 for ZeroMap2dBorrowed<'a, K0, K1, V>
306where
307 K0: for<'c> ZeroMapKV<'c> + ?Sized,
308 K1: for<'c> ZeroMapKV<'c> + ?Sized,
309 V: for<'c> ZeroMapKV<'c> + ?Sized,
310 <K0 as ZeroMapKV<'a>>::Slice: PartialEq<<K0 as ZeroMapKV<'b>>::Slice>,
311 <K1 as ZeroMapKV<'a>>::Slice: PartialEq<<K1 as ZeroMapKV<'b>>::Slice>,
312 <V as ZeroMapKV<'a>>::Slice: PartialEq<<V as ZeroMapKV<'b>>::Slice>,
313{
314 fn eq(&self, other: &ZeroMap2dBorrowed<'b, K0, K1, V>) -> bool {
315 self.keys0.eq(other.keys0)
316 && self.joiner.eq(other.joiner)
317 && self.keys1.eq(other.keys1)
318 && self.values.eq(other.values)
319 }
320}
321
322impl<'a, K0, K1, V> fmt::Debug for ZeroMap2dBorrowed<'a, K0, K1, V>
323where
324 K0: ZeroMapKV<'a> + ?Sized,
325 K1: ZeroMapKV<'a> + ?Sized,
326 V: ZeroMapKV<'a> + ?Sized,
327 K0::Slice: fmt::Debug,
328 K1::Slice: fmt::Debug,
329 V::Slice: fmt::Debug,
330{
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
332 f&mut DebugStruct<'_, '_>.debug_struct("ZeroMap2dBorrowed")
333 .field("keys0", &self.keys0)
334 .field("joiner", &self.joiner)
335 .field("keys1", &self.keys1)
336 .field(name:"values", &self.values)
337 .finish()
338 }
339}
340