Coverage Report

Created: 2025-05-08 06:13

/rust/registry/src/index.crates.io-6f17d22bba15001f/zerovec-0.10.4/src/map2d/map.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
use crate::ule::AsULE;
6
use crate::ZeroVec;
7
use alloc::borrow::Borrow;
8
use core::cmp::Ordering;
9
use core::convert::TryFrom;
10
use core::fmt;
11
use core::iter::FromIterator;
12
use core::ops::Range;
13
14
use super::*;
15
use crate::map::ZeroMapKV;
16
use crate::map::{MutableZeroVecLike, ZeroVecLike};
17
18
/// A zero-copy, two-dimensional map datastructure .
19
///
20
/// This is an extension of [`ZeroMap`] that supports two layers of keys. For example,
21
/// to map a pair of an integer and a string to a buffer, you can write:
22
///
23
/// ```no_run
24
/// # use zerovec::ZeroMap2d;
25
/// let _: ZeroMap2d<u32, str, [u8]> = unimplemented!();
26
/// ```
27
///
28
/// Internally, `ZeroMap2d` stores four zero-copy vectors, one for each type argument plus
29
/// one more to match between the two vectors of keys.
30
///
31
/// # Examples
32
///
33
/// ```
34
/// use zerovec::ZeroMap2d;
35
///
36
/// // Example byte buffer representing the map { 1: {2: "three" } }
37
/// let BINCODE_BYTES: &[u8; 51] = &[
38
///     2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0,
39
///     0, 0, 0, 0, 0, 0, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 116,
40
///     104, 114, 101, 101,
41
/// ];
42
///
43
/// // Deserializing to ZeroMap requires no heap allocations.
44
/// let zero_map: ZeroMap2d<u16, u16, str> =
45
///     bincode::deserialize(BINCODE_BYTES)
46
///         .expect("Should deserialize successfully");
47
/// assert_eq!(zero_map.get_2d(&1, &2), Some("three"));
48
/// ```
49
///
50
/// [`VarZeroVec`]: crate::VarZeroVec
51
/// [`ZeroMap`]: crate::ZeroMap
52
// ZeroMap2d contains 4 fields:
53
//
54
// - keys0 = sorted list of all K0 in the map
55
// - joiner = helper vec that maps from a K0 to a range of keys1
56
// - keys1 = list of all K1 in the map, sorted in ranges for each K0
57
// - values = list of all values in the map, sorted by (K0, K1)
58
//
59
// For a particular K0 at index i, the range of keys1 corresponding to K0 is
60
// (joiner[i-1]..joiner[i]), where the first range starts at 0.
61
//
62
// Required Invariants:
63
//
64
// 1. len(keys0) == len(joiner)
65
// 2. len(keys1) == len(values)
66
// 3. joiner is sorted
67
// 4. the last element of joiner is the length of keys1
68
//
69
// Optional Invariants:
70
//
71
// 5. keys0 is sorted (for binary_search)
72
// 6. ranges within keys1 are sorted (for binary_search)
73
// 7. every K0 is associated with at least one K1 (no empty ranges)
74
//
75
// During deserialization, these three invariants are not checked, because they put the
76
// ZeroMap2d in a deterministic state, even though it may have unexpected behavior.
77
pub struct ZeroMap2d<'a, K0, K1, V>
78
where
79
    K0: ZeroMapKV<'a>,
80
    K1: ZeroMapKV<'a>,
81
    V: ZeroMapKV<'a>,
82
    K0: ?Sized,
83
    K1: ?Sized,
84
    V: ?Sized,
85
{
86
    pub(crate) keys0: K0::Container,
87
    pub(crate) joiner: ZeroVec<'a, u32>,
88
    pub(crate) keys1: K1::Container,
89
    pub(crate) values: V::Container,
90
}
91
92
impl<'a, K0, K1, V> Default for ZeroMap2d<'a, K0, K1, V>
93
where
94
    K0: ZeroMapKV<'a>,
95
    K1: ZeroMapKV<'a>,
96
    V: ZeroMapKV<'a>,
97
    K0: ?Sized,
98
    K1: ?Sized,
99
    V: ?Sized,
100
{
101
0
    fn default() -> Self {
102
0
        Self::new()
103
0
    }
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, icu_locid::subtags::script::Script> as core::default::Default>::default
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<4>, icu_locid::subtags::region::Region> as core::default::Default>::default
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<_, _, _> as core::default::Default>::default
104
}
105
106
impl<'a, K0, K1, V> ZeroMap2d<'a, K0, K1, V>
107
where
108
    K0: ZeroMapKV<'a>,
109
    K1: ZeroMapKV<'a>,
110
    V: ZeroMapKV<'a>,
111
    K0: ?Sized,
112
    K1: ?Sized,
113
    V: ?Sized,
114
{
115
    /// Creates a new, empty `ZeroMap2d`.
116
    ///
117
    /// # Examples
118
    ///
119
    /// ```
120
    /// use zerovec::ZeroMap2d;
121
    ///
122
    /// let zm: ZeroMap2d<u16, str, str> = ZeroMap2d::new();
123
    /// assert!(zm.is_empty());
124
    /// ```
125
0
    pub fn new() -> Self {
126
0
        Self {
127
0
            keys0: K0::Container::zvl_with_capacity(0),
128
0
            joiner: ZeroVec::new(),
129
0
            keys1: K1::Container::zvl_with_capacity(0),
130
0
            values: V::Container::zvl_with_capacity(0),
131
0
        }
132
0
    }
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, icu_locid::subtags::script::Script>>::new
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<4>, icu_locid::subtags::region::Region>>::new
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<_, _, _>>::new
133
134
    #[doc(hidden)] // databake internal
135
0
    pub const unsafe fn from_parts_unchecked(
136
0
        keys0: K0::Container,
137
0
        joiner: ZeroVec<'a, u32>,
138
0
        keys1: K1::Container,
139
0
        values: V::Container,
140
0
    ) -> Self {
141
0
        Self {
142
0
            keys0,
143
0
            joiner,
144
0
            keys1,
145
0
            values,
146
0
        }
147
0
    }
148
149
    /// Construct a new [`ZeroMap2d`] with a given capacity
150
0
    pub fn with_capacity(capacity: usize) -> Self {
151
0
        Self {
152
0
            keys0: K0::Container::zvl_with_capacity(capacity),
153
0
            joiner: ZeroVec::with_capacity(capacity),
154
0
            keys1: K1::Container::zvl_with_capacity(capacity),
155
0
            values: V::Container::zvl_with_capacity(capacity),
156
0
        }
157
0
    }
158
159
    /// Obtain a borrowed version of this map
160
0
    pub fn as_borrowed(&'a self) -> ZeroMap2dBorrowed<'a, K0, K1, V> {
161
0
        ZeroMap2dBorrowed {
162
0
            keys0: self.keys0.zvl_as_borrowed(),
163
0
            joiner: &self.joiner,
164
0
            keys1: self.keys1.zvl_as_borrowed(),
165
0
            values: self.values.zvl_as_borrowed(),
166
0
        }
167
0
    }
168
169
    /// The number of values in the [`ZeroMap2d`]
170
0
    pub fn len(&self) -> usize {
171
0
        self.values.zvl_len()
172
0
    }
173
174
    /// Whether the [`ZeroMap2d`] is empty
175
0
    pub fn is_empty(&self) -> bool {
176
0
        self.values.zvl_len() == 0
177
0
    }
178
179
    /// Remove all elements from the [`ZeroMap2d`]
180
0
    pub fn clear(&mut self) {
181
0
        self.keys0.zvl_clear();
182
0
        self.joiner.clear();
183
0
        self.keys1.zvl_clear();
184
0
        self.values.zvl_clear();
185
0
    }
186
187
    /// Reserve capacity for `additional` more elements to be inserted into
188
    /// the [`ZeroMap2d`] to avoid frequent reallocations.
189
    ///
190
    /// See [`Vec::reserve()`](alloc::vec::Vec::reserve) for more information.
191
0
    pub fn reserve(&mut self, additional: usize) {
192
0
        self.keys0.zvl_reserve(additional);
193
0
        self.joiner.zvl_reserve(additional);
194
0
        self.keys1.zvl_reserve(additional);
195
0
        self.values.zvl_reserve(additional);
196
0
    }
197
198
    /// Produce an ordered iterator over keys0, which can then be used to get an iterator
199
    /// over keys1 for a particular key0.
200
    ///
201
    /// # Example
202
    ///
203
    /// Loop over all elements of a ZeroMap2d:
204
    ///
205
    /// ```
206
    /// use zerovec::ZeroMap2d;
207
    ///
208
    /// let mut map: ZeroMap2d<u16, u16, str> = ZeroMap2d::new();
209
    /// map.insert(&1, &1, "foo");
210
    /// map.insert(&2, &3, "bar");
211
    /// map.insert(&2, &4, "baz");
212
    ///
213
    /// let mut total_value = 0;
214
    ///
215
    /// for cursor in map.iter0() {
216
    ///     for (key1, value) in cursor.iter1() {
217
    ///         // This code runs for every (key0, key1) pair
218
    ///         total_value += cursor.key0().as_unsigned_int() as usize;
219
    ///         total_value += key1.as_unsigned_int() as usize;
220
    ///         total_value += value.len();
221
    ///     }
222
    /// }
223
    ///
224
    /// assert_eq!(total_value, 22);
225
    /// ```
226
0
    pub fn iter0<'l>(&'l self) -> impl Iterator<Item = ZeroMap2dCursor<'l, 'a, K0, K1, V>> + 'l {
227
0
        (0..self.keys0.zvl_len()).map(move |idx| ZeroMap2dCursor::from_cow(self, idx))
228
0
    }
229
230
    // INTERNAL ROUTINES FOLLOW //
231
232
    /// Given an index into the joiner array, returns the corresponding range of keys1
233
0
    fn get_range_for_key0_index(&self, key0_index: usize) -> Range<usize> {
234
0
        ZeroMap2dCursor::from_cow(self, key0_index).get_range()
235
0
    }
236
237
    /// Removes key0_index from the keys0 array and the joiner array
238
0
    fn remove_key0_index(&mut self, key0_index: usize) {
239
0
        self.keys0.zvl_remove(key0_index);
240
0
        self.joiner.with_mut(|v| v.remove(key0_index));
241
0
    }
242
243
    /// Shifts all joiner ranges from key0_index onward one index up
244
0
    fn joiner_expand(&mut self, key0_index: usize) {
245
0
        #[allow(clippy::expect_used)] // slice overflow
246
0
        self.joiner
247
0
            .to_mut_slice()
248
0
            .iter_mut()
249
0
            .skip(key0_index)
250
0
            .for_each(|ref mut v| {
251
0
                // TODO(#1410): Make this fallible
252
0
                **v = v
253
0
                    .as_unsigned_int()
254
0
                    .checked_add(1)
255
0
                    .expect("Attempted to add more than 2^32 elements to a ZeroMap2d")
256
0
                    .to_unaligned()
257
0
            })
258
0
    }
259
260
    /// Shifts all joiner ranges from key0_index onward one index down
261
0
    fn joiner_shrink(&mut self, key0_index: usize) {
262
0
        self.joiner
263
0
            .to_mut_slice()
264
0
            .iter_mut()
265
0
            .skip(key0_index)
266
0
            .for_each(|ref mut v| **v = (v.as_unsigned_int() - 1).to_unaligned())
267
0
    }
268
}
269
270
impl<'a, K0, K1, V> ZeroMap2d<'a, K0, K1, V>
271
where
272
    K0: ZeroMapKV<'a> + Ord,
273
    K1: ZeroMapKV<'a> + Ord,
274
    V: ZeroMapKV<'a>,
275
    K0: ?Sized,
276
    K1: ?Sized,
277
    V: ?Sized,
278
{
279
    /// Get the value associated with `key0` and `key1`, if it exists.
280
    ///
281
    /// For more fine-grained error handling, use [`ZeroMap2d::get0`].
282
    ///
283
    /// ```rust
284
    /// use zerovec::ZeroMap2d;
285
    ///
286
    /// let mut map = ZeroMap2d::new();
287
    /// map.insert(&1, "one", "foo");
288
    /// map.insert(&2, "one", "bar");
289
    /// map.insert(&2, "two", "baz");
290
    /// assert_eq!(map.get_2d(&1, "one"), Some("foo"));
291
    /// assert_eq!(map.get_2d(&1, "two"), None);
292
    /// assert_eq!(map.get_2d(&2, "one"), Some("bar"));
293
    /// assert_eq!(map.get_2d(&2, "two"), Some("baz"));
294
    /// assert_eq!(map.get_2d(&3, "three"), None);
295
    /// ```
296
0
    pub fn get_2d(&self, key0: &K0, key1: &K1) -> Option<&V::GetType> {
297
0
        self.get0(key0)?.get1(key1)
298
0
    }
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<4>, icu_locid::subtags::region::Region>>::get_2d
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<_, _, _>>::get_2d
299
300
    /// Insert `value` with `key`, returning the existing value if it exists.
301
    ///
302
    /// ```rust
303
    /// use zerovec::ZeroMap2d;
304
    ///
305
    /// let mut map = ZeroMap2d::new();
306
    /// assert_eq!(map.insert(&0, "zero", "foo"), None,);
307
    /// assert_eq!(map.insert(&1, "one", "bar"), None,);
308
    /// assert_eq!(map.insert(&1, "one", "baz").as_deref(), Some("bar"),);
309
    /// assert_eq!(map.get_2d(&1, "one").as_deref(), Some("baz"));
310
    /// assert_eq!(map.len(), 2);
311
    /// ```
312
0
    pub fn insert(&mut self, key0: &K0, key1: &K1, value: &V) -> Option<V::OwnedType> {
313
0
        let (key0_index, range) = self.get_or_insert_range_for_key0(key0);
314
0
        debug_assert!(range.start <= range.end); // '<=' because we may have inserted a new key0
315
0
        debug_assert!(range.end <= self.keys1.zvl_len());
316
0
        let range_start = range.start;
317
        #[allow(clippy::unwrap_used)] // by debug_assert! invariants
318
0
        let index = range_start
319
0
            + match self.keys1.zvl_binary_search_in_range(key1, range).unwrap() {
320
0
                Ok(index) => return Some(self.values.zvl_replace(range_start + index, value)),
321
0
                Err(index) => index,
322
0
            };
323
0
        self.keys1.zvl_insert(index, key1);
324
0
        self.values.zvl_insert(index, value);
325
0
        self.joiner_expand(key0_index);
326
0
        #[cfg(debug_assertions)]
327
0
        self.check_invariants();
328
0
        None
329
0
    }
330
331
    /// Remove the value at `key`, returning it if it exists.
332
    ///
333
    /// ```rust
334
    /// use zerovec::ZeroMap2d;
335
    ///
336
    /// let mut map = ZeroMap2d::new();
337
    /// map.insert(&1, "one", "foo");
338
    /// map.insert(&2, "two", "bar");
339
    /// assert_eq!(
340
    ///     map.remove(&1, "one"),
341
    ///     Some("foo".to_owned().into_boxed_str())
342
    /// );
343
    /// assert_eq!(map.get_2d(&1, "one"), None);
344
    /// assert_eq!(map.remove(&1, "one"), None);
345
    /// ```
346
0
    pub fn remove(&mut self, key0: &K0, key1: &K1) -> Option<V::OwnedType> {
347
0
        let key0_index = self.keys0.zvl_binary_search(key0).ok()?;
348
0
        let range = self.get_range_for_key0_index(key0_index);
349
0
        debug_assert!(range.start < range.end); // '<' because every key0 should have a key1
350
0
        debug_assert!(range.end <= self.keys1.zvl_len());
351
0
        let is_singleton_range = range.start + 1 == range.end;
352
        #[allow(clippy::unwrap_used)] // by debug_assert invariants
353
0
        let index = range.start
354
0
            + self
355
0
                .keys1
356
0
                .zvl_binary_search_in_range(key1, range)
357
0
                .unwrap()
358
0
                .ok()?;
359
0
        self.keys1.zvl_remove(index);
360
0
        let removed = self.values.zvl_remove(index);
361
0
        self.joiner_shrink(key0_index);
362
0
        if is_singleton_range {
363
0
            self.remove_key0_index(key0_index);
364
0
        }
365
        #[cfg(debug_assertions)]
366
        self.check_invariants();
367
0
        Some(removed)
368
0
    }
369
370
    /// Appends `value` with `key` to the end of the underlying vector, returning
371
    /// `key` and `value` _if it failed_. Useful for extending with an existing
372
    /// sorted list.
373
    ///
374
    /// ```rust
375
    /// use zerovec::ZeroMap2d;
376
    ///
377
    /// let mut map = ZeroMap2d::new();
378
    /// assert!(map.try_append(&1, "one", "uno").is_none());
379
    /// assert!(map.try_append(&3, "three", "tres").is_none());
380
    ///
381
    /// let unsuccessful = map.try_append(&3, "three", "tres-updated");
382
    /// assert!(unsuccessful.is_some(), "append duplicate of last key");
383
    ///
384
    /// let unsuccessful = map.try_append(&2, "two", "dos");
385
    /// assert!(unsuccessful.is_some(), "append out of order");
386
    ///
387
    /// assert_eq!(map.get_2d(&1, "one"), Some("uno"));
388
    ///
389
    /// // contains the original value for the key: 3
390
    /// assert_eq!(map.get_2d(&3, "three"), Some("tres"));
391
    ///
392
    /// // not appended since it wasn't in order
393
    /// assert_eq!(map.get_2d(&2, "two"), None);
394
    /// ```
395
    #[must_use]
396
0
    pub fn try_append<'b>(
397
0
        &mut self,
398
0
        key0: &'b K0,
399
0
        key1: &'b K1,
400
0
        value: &'b V,
401
0
    ) -> Option<(&'b K0, &'b K1, &'b V)> {
402
0
        if self.is_empty() {
403
0
            self.keys0.zvl_push(key0);
404
0
            self.joiner.with_mut(|v| v.push(1u32.to_unaligned()));
405
0
            self.keys1.zvl_push(key1);
406
0
            self.values.zvl_push(value);
407
0
            return None;
408
0
        }
409
0
410
0
        // The unwraps are protected by the fact that we are not empty
411
0
        #[allow(clippy::unwrap_used)]
412
0
        let last_key0 = self.keys0.zvl_get(self.keys0.zvl_len() - 1).unwrap();
413
0
        let key0_cmp = K0::Container::t_cmp_get(key0, last_key0);
414
0
        #[allow(clippy::unwrap_used)]
415
0
        let last_key1 = self.keys1.zvl_get(self.keys1.zvl_len() - 1).unwrap();
416
0
        let key1_cmp = K1::Container::t_cmp_get(key1, last_key1);
417
0
418
0
        // Check for error case (out of order)
419
0
        match key0_cmp {
420
            Ordering::Less => {
421
                // Error case
422
0
                return Some((key0, key1, value));
423
            }
424
            Ordering::Equal => {
425
0
                match key1_cmp {
426
                    Ordering::Less | Ordering::Equal => {
427
                        // Error case
428
0
                        return Some((key0, key1, value));
429
                    }
430
0
                    _ => {}
431
                }
432
            }
433
0
            _ => {}
434
        }
435
436
        #[allow(clippy::expect_used)] // slice overflow
437
0
        let joiner_value = u32::try_from(self.keys1.zvl_len() + 1)
438
0
            .expect("Attempted to add more than 2^32 elements to a ZeroMap2d");
439
0
440
0
        // All OK to append
441
0
        #[allow(clippy::unwrap_used)]
442
0
        if key0_cmp == Ordering::Greater {
443
0
            self.keys0.zvl_push(key0);
444
0
            self.joiner
445
0
                .with_mut(|v| v.push(joiner_value.to_unaligned()));
446
0
        } else {
447
0
            // This unwrap is protected because we are not empty
448
0
            *self.joiner.to_mut_slice().last_mut().unwrap() = joiner_value.to_unaligned();
449
0
        }
450
0
        self.keys1.zvl_push(key1);
451
0
        self.values.zvl_push(value);
452
0
453
0
        #[cfg(debug_assertions)]
454
0
        self.check_invariants();
455
0
456
0
        None
457
0
    }
458
459
    // INTERNAL ROUTINES FOLLOW //
460
461
    #[cfg(debug_assertions)]
462
    #[allow(clippy::unwrap_used)] // this is an assertion function
463
    pub(crate) fn check_invariants(&self) {
464
        debug_assert_eq!(self.keys0.zvl_len(), self.joiner.len());
465
        debug_assert_eq!(self.keys1.zvl_len(), self.values.zvl_len());
466
        debug_assert!(self.keys0.zvl_is_ascending());
467
        debug_assert!(self.joiner.zvl_is_ascending());
468
        if let Some(last_joiner) = self.joiner.last() {
469
            debug_assert_eq!(last_joiner as usize, self.keys1.zvl_len());
470
        }
471
        for i in 0..self.joiner.len() {
472
            let j0 = if i == 0 {
473
                0
474
            } else {
475
                self.joiner.get(i - 1).unwrap() as usize
476
            };
477
            let j1 = self.joiner.get(i).unwrap() as usize;
478
            debug_assert_ne!(j0, j1);
479
            for j in (j0 + 1)..j1 {
480
                let m0 = self.keys1.zvl_get(j - 1).unwrap();
481
                let m1 = self.keys1.zvl_get(j).unwrap();
482
                debug_assert_eq!(Ordering::Less, K1::Container::get_cmp_get(m0, m1));
483
            }
484
        }
485
    }
486
}
487
488
impl<'a, K0, K1, V> ZeroMap2d<'a, K0, K1, V>
489
where
490
    K0: ZeroMapKV<'a> + Ord,
491
    K1: ZeroMapKV<'a>,
492
    V: ZeroMapKV<'a>,
493
    K0: ?Sized,
494
    K1: ?Sized,
495
    V: ?Sized,
496
{
497
    /// Gets a cursor for `key0`. If `None`, then `key0` is not in the map. If `Some`,
498
    /// then `key0` is in the map, and `key1` can be queried.
499
    ///
500
    /// ```rust
501
    /// use zerovec::ZeroMap2d;
502
    ///
503
    /// let mut map = ZeroMap2d::new();
504
    /// map.insert(&1u32, "one", "foo");
505
    /// map.insert(&2, "one", "bar");
506
    /// map.insert(&2, "two", "baz");
507
    /// assert_eq!(map.get0(&1).unwrap().get1("one").unwrap(), "foo");
508
    /// assert_eq!(map.get0(&1).unwrap().get1("two"), None);
509
    /// assert_eq!(map.get0(&2).unwrap().get1("one").unwrap(), "bar");
510
    /// assert_eq!(map.get0(&2).unwrap().get1("two").unwrap(), "baz");
511
    /// assert_eq!(map.get0(&3), None);
512
    /// ```
513
    #[inline]
514
0
    pub fn get0<'l>(&'l self, key0: &K0) -> Option<ZeroMap2dCursor<'l, 'a, K0, K1, V>> {
515
0
        let key0_index = self.keys0.zvl_binary_search(key0).ok()?;
516
0
        Some(ZeroMap2dCursor::from_cow(self, key0_index))
517
0
    }
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, icu_locid::subtags::script::Script>>::get0
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<4>, icu_locid::subtags::region::Region>>::get0
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<_, _, _>>::get0
518
519
    /// Binary search the map for `key0`, returning a cursor.
520
    ///
521
    /// ```rust
522
    /// use zerovec::ZeroMap2d;
523
    ///
524
    /// let mut map = ZeroMap2d::new();
525
    /// map.insert(&1, "one", "foo");
526
    /// map.insert(&2, "two", "bar");
527
    /// assert!(matches!(map.get0_by(|probe| probe.cmp(&1)), Some(_)));
528
    /// assert!(matches!(map.get0_by(|probe| probe.cmp(&3)), None));
529
    /// ```
530
0
    pub fn get0_by<'l>(
531
0
        &'l self,
532
0
        predicate: impl FnMut(&K0) -> Ordering,
533
0
    ) -> Option<ZeroMap2dCursor<'l, 'a, K0, K1, V>> {
534
0
        let key0_index = self.keys0.zvl_binary_search_by(predicate).ok()?;
535
0
        Some(ZeroMap2dCursor::from_cow(self, key0_index))
536
0
    }
537
538
    /// Returns whether `key0` is contained in this map
539
    ///
540
    /// ```rust
541
    /// use zerovec::ZeroMap2d;
542
    ///
543
    /// let mut map = ZeroMap2d::new();
544
    /// map.insert(&1, "one", "foo");
545
    /// map.insert(&2, "two", "bar");
546
    /// assert!(map.contains_key0(&1));
547
    /// assert!(!map.contains_key0(&3));
548
    /// ```
549
0
    pub fn contains_key0(&self, key0: &K0) -> bool {
550
0
        self.keys0.zvl_binary_search(key0).is_ok()
551
0
    }
552
553
    // INTERNAL ROUTINES FOLLOW //
554
555
    /// Same as `get_range_for_key0`, but creates key0 if it doesn't already exist
556
0
    fn get_or_insert_range_for_key0(&mut self, key0: &K0) -> (usize, Range<usize>) {
557
0
        match self.keys0.zvl_binary_search(key0) {
558
0
            Ok(key0_index) => (key0_index, self.get_range_for_key0_index(key0_index)),
559
0
            Err(key0_index) => {
560
                // Add an entry to self.keys0 and self.joiner
561
0
                let joiner_value = if key0_index == 0 {
562
0
                    0
563
                } else {
564
0
                    debug_assert!(key0_index <= self.joiner.len());
565
                    // The unwrap is protected by the debug_assert above and key0_index != 0
566
                    #[allow(clippy::unwrap_used)]
567
0
                    self.joiner.get(key0_index - 1).unwrap()
568
                };
569
0
                self.keys0.zvl_insert(key0_index, key0);
570
0
                self.joiner
571
0
                    .with_mut(|v| v.insert(key0_index, joiner_value.to_unaligned()));
572
0
                (key0_index, (joiner_value as usize)..(joiner_value as usize))
573
            }
574
        }
575
0
    }
576
}
577
578
impl<'a, K0, K1, V> ZeroMap2d<'a, K0, K1, V>
579
where
580
    K0: ZeroMapKV<'a> + Ord,
581
    K1: ZeroMapKV<'a> + Ord,
582
    V: ZeroMapKV<'a>,
583
    V: Copy,
584
    K0: ?Sized,
585
    K1: ?Sized,
586
{
587
    /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE`
588
    ///
589
    /// # Examples
590
    ///
591
    /// ```
592
    /// # use zerovec::ZeroMap2d;
593
    /// let mut map: ZeroMap2d<u16, u16, u16> = ZeroMap2d::new();
594
    /// map.insert(&1, &2, &3);
595
    /// map.insert(&1, &4, &5);
596
    /// map.insert(&6, &7, &8);
597
    ///
598
    /// assert_eq!(map.get_copied_2d(&6, &7), Some(8));
599
    /// ```
600
    #[inline]
601
0
    pub fn get_copied_2d(&self, key0: &K0, key1: &K1) -> Option<V> {
602
0
        self.get0(key0)?.get1_copied(key1)
603
0
    }
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, tinystr::unvalidated::UnvalidatedTinyAsciiStr<3>, icu_locid::subtags::script::Script>>::get_copied_2d
Unexecuted instantiation: <zerovec::map2d::map::ZeroMap2d<_, _, _>>::get_copied_2d
604
}
605
606
impl<'a, K0, K1, V> From<ZeroMap2dBorrowed<'a, K0, K1, V>> for ZeroMap2d<'a, K0, K1, V>
607
where
608
    K0: ZeroMapKV<'a>,
609
    K1: ZeroMapKV<'a>,
610
    V: ZeroMapKV<'a>,
611
    K0: ?Sized,
612
    K1: ?Sized,
613
    V: ?Sized,
614
{
615
0
    fn from(other: ZeroMap2dBorrowed<'a, K0, K1, V>) -> Self {
616
0
        Self {
617
0
            keys0: K0::Container::zvl_from_borrowed(other.keys0),
618
0
            joiner: other.joiner.as_zerovec(),
619
0
            keys1: K1::Container::zvl_from_borrowed(other.keys1),
620
0
            values: V::Container::zvl_from_borrowed(other.values),
621
0
        }
622
0
    }
623
}
624
625
// We can't use the default PartialEq because ZeroMap2d is invariant
626
// so otherwise rustc will not automatically allow you to compare ZeroMaps
627
// with different lifetimes
628
impl<'a, 'b, K0, K1, V> PartialEq<ZeroMap2d<'b, K0, K1, V>> for ZeroMap2d<'a, K0, K1, V>
629
where
630
    K0: for<'c> ZeroMapKV<'c> + ?Sized,
631
    K1: for<'c> ZeroMapKV<'c> + ?Sized,
632
    V: for<'c> ZeroMapKV<'c> + ?Sized,
633
    <K0 as ZeroMapKV<'a>>::Container: PartialEq<<K0 as ZeroMapKV<'b>>::Container>,
634
    <K1 as ZeroMapKV<'a>>::Container: PartialEq<<K1 as ZeroMapKV<'b>>::Container>,
635
    <V as ZeroMapKV<'a>>::Container: PartialEq<<V as ZeroMapKV<'b>>::Container>,
636
{
637
0
    fn eq(&self, other: &ZeroMap2d<'b, K0, K1, V>) -> bool {
638
0
        self.keys0.eq(&other.keys0)
639
0
            && self.joiner.eq(&other.joiner)
640
0
            && self.keys1.eq(&other.keys1)
641
0
            && self.values.eq(&other.values)
642
0
    }
643
}
644
645
impl<'a, K0, K1, V> fmt::Debug for ZeroMap2d<'a, K0, K1, V>
646
where
647
    K0: ZeroMapKV<'a> + ?Sized,
648
    K1: ZeroMapKV<'a> + ?Sized,
649
    V: ZeroMapKV<'a> + ?Sized,
650
    <K0 as ZeroMapKV<'a>>::Container: fmt::Debug,
651
    <K1 as ZeroMapKV<'a>>::Container: fmt::Debug,
652
    <V as ZeroMapKV<'a>>::Container: fmt::Debug,
653
{
654
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
655
0
        f.debug_struct("ZeroMap2d")
656
0
            .field("keys0", &self.keys0)
657
0
            .field("joiner", &self.joiner)
658
0
            .field("keys1", &self.keys1)
659
0
            .field("values", &self.values)
660
0
            .finish()
661
0
    }
662
}
663
664
impl<'a, K0, K1, V> Clone for ZeroMap2d<'a, K0, K1, V>
665
where
666
    K0: ZeroMapKV<'a> + ?Sized,
667
    K1: ZeroMapKV<'a> + ?Sized,
668
    V: ZeroMapKV<'a> + ?Sized,
669
    <K0 as ZeroMapKV<'a>>::Container: Clone,
670
    <K1 as ZeroMapKV<'a>>::Container: Clone,
671
    <V as ZeroMapKV<'a>>::Container: Clone,
672
{
673
0
    fn clone(&self) -> Self {
674
0
        Self {
675
0
            keys0: self.keys0.clone(),
676
0
            joiner: self.joiner.clone(),
677
0
            keys1: self.keys1.clone(),
678
0
            values: self.values.clone(),
679
0
        }
680
0
    }
681
}
682
683
impl<'a, A, B, C, K0, K1, V> FromIterator<(A, B, C)> for ZeroMap2d<'a, K0, K1, V>
684
where
685
    A: Borrow<K0>,
686
    B: Borrow<K1>,
687
    C: Borrow<V>,
688
    K0: ZeroMapKV<'a> + ?Sized + Ord,
689
    K1: ZeroMapKV<'a> + ?Sized + Ord,
690
    V: ZeroMapKV<'a> + ?Sized,
691
{
692
0
    fn from_iter<T>(iter: T) -> Self
693
0
    where
694
0
        T: IntoIterator<Item = (A, B, C)>,
695
0
    {
696
0
        let iter = iter.into_iter();
697
0
        let mut map = match iter.size_hint() {
698
0
            (_, Some(upper)) => Self::with_capacity(upper),
699
0
            (lower, None) => Self::with_capacity(lower),
700
        };
701
702
0
        for (key0, key1, value) in iter {
703
0
            if let Some((key0, key1, value)) =
704
0
                map.try_append(key0.borrow(), key1.borrow(), value.borrow())
705
0
            {
706
0
                map.insert(key0, key1, value);
707
0
            }
708
        }
709
        #[cfg(debug_assertions)]
710
        map.check_invariants();
711
0
        map
712
0
    }
713
}
714
715
#[cfg(test)]
716
mod test {
717
    use super::*;
718
    use alloc::collections::BTreeMap;
719
720
    #[test]
721
    fn stress_test() {
722
        let mut zm2d = ZeroMap2d::<u16, str, str>::new();
723
724
        assert_eq!(
725
            format!("{zm2d:?}"),
726
            "ZeroMap2d { keys0: ZeroVec([]), joiner: ZeroVec([]), keys1: [], values: [] }"
727
        );
728
        assert_eq!(zm2d.get0(&0), None);
729
730
        let result = zm2d.try_append(&3, "ccc", "CCC");
731
        assert!(result.is_none());
732
733
        assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3]), joiner: ZeroVec([1]), keys1: [\"ccc\"], values: [\"CCC\"] }");
734
        assert_eq!(zm2d.get0(&0), None);
735
        assert_eq!(zm2d.get0(&3).unwrap().get1(""), None);
736
        assert_eq!(zm2d.get_2d(&3, "ccc"), Some("CCC"));
737
        assert_eq!(zm2d.get0(&99), None);
738
739
        let result = zm2d.try_append(&3, "eee", "EEE");
740
        assert!(result.is_none());
741
742
        assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3]), joiner: ZeroVec([2]), keys1: [\"ccc\", \"eee\"], values: [\"CCC\", \"EEE\"] }");
743
        assert_eq!(zm2d.get0(&0), None);
744
        assert_eq!(zm2d.get0(&3).unwrap().get1(""), None);
745
        assert_eq!(zm2d.get_2d(&3, "ccc"), Some("CCC"));
746
        assert_eq!(zm2d.get_2d(&3, "eee"), Some("EEE"));
747
        assert_eq!(zm2d.get0(&3).unwrap().get1("five"), None);
748
        assert_eq!(zm2d.get0(&99), None);
749
750
        // Out of order
751
        let result = zm2d.try_append(&3, "ddd", "DD0");
752
        assert!(result.is_some());
753
754
        // Append a few more elements
755
        let result = zm2d.try_append(&5, "ddd", "DD1");
756
        assert!(result.is_none());
757
        let result = zm2d.try_append(&7, "ddd", "DD2");
758
        assert!(result.is_none());
759
        let result = zm2d.try_append(&7, "eee", "EEE");
760
        assert!(result.is_none());
761
        let result = zm2d.try_append(&7, "www", "WWW");
762
        assert!(result.is_none());
763
        let result = zm2d.try_append(&9, "yyy", "YYY");
764
        assert!(result.is_none());
765
766
        assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3, 5, 7, 9]), joiner: ZeroVec([2, 3, 6, 7]), keys1: [\"ccc\", \"eee\", \"ddd\", \"ddd\", \"eee\", \"www\", \"yyy\"], values: [\"CCC\", \"EEE\", \"DD1\", \"DD2\", \"EEE\", \"WWW\", \"YYY\"] }");
767
        assert_eq!(zm2d.get0(&0), None);
768
        assert_eq!(zm2d.get0(&3).unwrap().get1(""), None);
769
        assert_eq!(zm2d.get_2d(&3, "ccc"), Some("CCC"));
770
        assert_eq!(zm2d.get_2d(&3, "eee"), Some("EEE"));
771
        assert_eq!(zm2d.get0(&3).unwrap().get1("zzz"), None);
772
        assert_eq!(zm2d.get0(&4), None);
773
        assert_eq!(zm2d.get0(&5).unwrap().get1("aaa"), None);
774
        assert_eq!(zm2d.get_2d(&5, "ddd"), Some("DD1"));
775
        assert_eq!(zm2d.get0(&5).unwrap().get1("zzz"), None);
776
        assert_eq!(zm2d.get0(&6), None);
777
        assert_eq!(zm2d.get0(&7).unwrap().get1("aaa"), None);
778
        assert_eq!(zm2d.get_2d(&7, "ddd"), Some("DD2"));
779
        assert_eq!(zm2d.get_2d(&7, "eee"), Some("EEE"));
780
        assert_eq!(zm2d.get_2d(&7, "www"), Some("WWW"));
781
        assert_eq!(zm2d.get0(&7).unwrap().get1("yyy"), None);
782
        assert_eq!(zm2d.get0(&7).unwrap().get1("zzz"), None);
783
        assert_eq!(zm2d.get0(&8), None);
784
        assert_eq!(zm2d.get0(&9).unwrap().get1("aaa"), None);
785
        assert_eq!(zm2d.get0(&9).unwrap().get1("www"), None);
786
        assert_eq!(zm2d.get_2d(&9, "yyy"), Some("YYY"));
787
        assert_eq!(zm2d.get0(&9).unwrap().get1("zzz"), None);
788
        assert_eq!(zm2d.get0(&10), None);
789
        assert_eq!(zm2d.get0(&99), None);
790
791
        // Insert some elements
792
        zm2d.insert(&3, "mmm", "MM0");
793
        zm2d.insert(&6, "ddd", "DD3");
794
        zm2d.insert(&6, "mmm", "MM1");
795
        zm2d.insert(&6, "nnn", "NNN");
796
797
        assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3, 5, 6, 7, 9]), joiner: ZeroVec([3, 4, 7, 10, 11]), keys1: [\"ccc\", \"eee\", \"mmm\", \"ddd\", \"ddd\", \"mmm\", \"nnn\", \"ddd\", \"eee\", \"www\", \"yyy\"], values: [\"CCC\", \"EEE\", \"MM0\", \"DD1\", \"DD3\", \"MM1\", \"NNN\", \"DD2\", \"EEE\", \"WWW\", \"YYY\"] }");
798
        assert_eq!(zm2d.get0(&0), None);
799
        assert_eq!(zm2d.get0(&3).unwrap().get1(""), None);
800
        assert_eq!(zm2d.get_2d(&3, "ccc"), Some("CCC"));
801
        assert_eq!(zm2d.get_2d(&3, "eee"), Some("EEE"));
802
        assert_eq!(zm2d.get_2d(&3, "mmm"), Some("MM0"));
803
        assert_eq!(zm2d.get0(&3).unwrap().get1("zzz"), None);
804
        assert_eq!(zm2d.get0(&4), None);
805
        assert_eq!(zm2d.get0(&5).unwrap().get1("aaa"), None);
806
        assert_eq!(zm2d.get_2d(&5, "ddd"), Some("DD1"));
807
        assert_eq!(zm2d.get0(&5).unwrap().get1("zzz"), None);
808
        assert_eq!(zm2d.get0(&6).unwrap().get1("aaa"), None);
809
        assert_eq!(zm2d.get_2d(&6, "ddd"), Some("DD3"));
810
        assert_eq!(zm2d.get_2d(&6, "mmm"), Some("MM1"));
811
        assert_eq!(zm2d.get_2d(&6, "nnn"), Some("NNN"));
812
        assert_eq!(zm2d.get0(&6).unwrap().get1("zzz"), None);
813
        assert_eq!(zm2d.get0(&7).unwrap().get1("aaa"), None);
814
        assert_eq!(zm2d.get_2d(&7, "ddd"), Some("DD2"));
815
        assert_eq!(zm2d.get_2d(&7, "eee"), Some("EEE"));
816
        assert_eq!(zm2d.get_2d(&7, "www"), Some("WWW"));
817
        assert_eq!(zm2d.get0(&7).unwrap().get1("yyy"), None);
818
        assert_eq!(zm2d.get0(&7).unwrap().get1("zzz"), None);
819
        assert_eq!(zm2d.get0(&8), None);
820
        assert_eq!(zm2d.get0(&9).unwrap().get1("aaa"), None);
821
        assert_eq!(zm2d.get0(&9).unwrap().get1("www"), None);
822
        assert_eq!(zm2d.get_2d(&9, "yyy"), Some("YYY"));
823
        assert_eq!(zm2d.get0(&9).unwrap().get1("zzz"), None);
824
        assert_eq!(zm2d.get0(&10), None);
825
        assert_eq!(zm2d.get0(&99), None);
826
827
        // Remove some elements
828
        let result = zm2d.remove(&3, "ccc"); // first element
829
        assert_eq!(result.as_deref(), Some("CCC"));
830
        let result = zm2d.remove(&3, "mmm"); // middle element
831
        assert_eq!(result.as_deref(), Some("MM0"));
832
        let result = zm2d.remove(&5, "ddd"); // singleton K0
833
        assert_eq!(result.as_deref(), Some("DD1"));
834
        let result = zm2d.remove(&9, "yyy"); // last element
835
        assert_eq!(result.as_deref(), Some("YYY"));
836
837
        assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3, 6, 7]), joiner: ZeroVec([1, 4, 7]), keys1: [\"eee\", \"ddd\", \"mmm\", \"nnn\", \"ddd\", \"eee\", \"www\"], values: [\"EEE\", \"DD3\", \"MM1\", \"NNN\", \"DD2\", \"EEE\", \"WWW\"] }");
838
    }
839
840
    #[test]
841
    fn zeromap2d_metazone() {
842
        let source_data = [
843
            (*b"aedxb", 0, Some(*b"gulf")),
844
            (*b"afkbl", 0, Some(*b"afgh")),
845
            (*b"ushnl", 0, None),
846
            (*b"ushnl", 7272660, Some(*b"haal")),
847
            (*b"ushnl", 0, None),
848
            (*b"ushnl", 7272660, Some(*b"haal")),
849
        ];
850
851
        let btreemap: BTreeMap<([u8; 5], i32), Option<[u8; 4]>> = source_data
852
            .iter()
853
            .copied()
854
            .map(|(a, b, c)| ((a, b), c))
855
            .collect();
856
857
        let zeromap2d: ZeroMap2d<[u8; 5], i32, Option<[u8; 4]>> =
858
            source_data.iter().copied().collect();
859
860
        let mut btreemap_iter = btreemap.iter();
861
862
        for cursor in zeromap2d.iter0() {
863
            for (key1, value) in cursor.iter1() {
864
                // This code runs for every (key0, key1) pair in order
865
                let expected = btreemap_iter.next().unwrap();
866
                assert_eq!(
867
                    (expected.0 .0, expected.0 .1, expected.1),
868
                    (*cursor.key0(), key1.as_unsigned_int() as i32, &value.get())
869
                );
870
            }
871
        }
872
        assert!(btreemap_iter.next().is_none());
873
    }
874
}