ixa/people/multi_property.rs
1//! The utilities in this module are used by query and multi-properties so that queries
2//! having multiple properties can be resolved to an indexed multi-property if possible.
3
4use crate::hashing::{one_shot_128, HashMap};
5use crate::people::HashValueType;
6use std::any::TypeId;
7use std::cell::RefCell;
8use std::sync::{LazyLock, Mutex};
9
10/// A map from a list of `TypeId`s to the `TypeId` of the multi-property type.
11/// The list of `TypeId`s is assumed to be sorted.
12///
13/// Use `register_type_ids_to_muli_property_id()` to register a multi-property.
14static MULTI_PROPERTY_INDEX_MAP: LazyLock<Mutex<RefCell<HashMap<HashValueType, TypeId>>>> =
15 LazyLock::new(|| Mutex::new(RefCell::new(HashMap::default())));
16
17/// A method that looks up the `TypeId` of the multi-property that has the given
18/// list of `TypeId`s as its properties.
19pub fn type_ids_to_multi_property_id(type_ids: &[TypeId]) -> Option<TypeId> {
20 let hash = one_shot_128(&type_ids);
21 MULTI_PROPERTY_INDEX_MAP
22 .lock()
23 .unwrap()
24 .borrow()
25 .get(&hash)
26 .copied()
27}
28
29/// A method that registers the `TypeId` of the multi-property that has the given
30/// list of `TypeId`s as its properties.
31///
32/// Use `type_ids_to_muli_property_id()` to look up a `TypeId`.
33pub fn register_type_ids_to_muli_property_id(type_ids: &[TypeId], multi_property_id: TypeId) {
34 let hash = one_shot_128(&type_ids);
35 MULTI_PROPERTY_INDEX_MAP
36 .lock()
37 .unwrap()
38 .borrow_mut()
39 .insert(hash, multi_property_id);
40}
41
42/// An iota function that returns an array of the form `[0, 1, 2, 3, ..., N-1]`. The size of the array
43/// is statically known, avoiding `Vec` allocations.
44const fn make_indices<const N: usize>() -> [usize; N] {
45 let mut arr = [0; N];
46 let mut i = 0;
47 while i < N {
48 arr[i] = i;
49 i += 1;
50 }
51 arr
52}
53
54/// Returns the indices of `keys` in sorted order. These indices are used to reorder some other
55/// array according to the sorted order of the `keys`.
56///
57/// This version works in cases where the size of the slice is not known at compile time,
58/// returning an allocated `Vec`.
59pub fn sorted_indices<T: Ord>(keys: &[T]) -> Vec<usize> {
60 let mut indices: Vec<usize> = (0..keys.len()).collect();
61 indices.sort_by_key(|&i| &keys[i]);
62 indices
63}
64
65/// Returns the indices of `keys` in sorted order. These indices are used to reorder some other
66/// array according to the sorted order of the `keys`, e.g. by `static_apply_reordering`.
67///
68/// "Static" in the name refers to the fact that it takes and returns an array of statically
69/// known size, avoiding `Vec` allocations.
70pub fn static_sorted_indices<T: Ord, const N: usize>(keys: &[T; N]) -> [usize; N] {
71 let mut indices = make_indices::<N>();
72 indices.sort_by_key(|&i| &keys[i]);
73 indices
74}
75
76/// Reorders the `values` in place according to the ordering defined by `indices`. The `indices`
77/// is an ordering produced by `sorted_indices`/`static_sorted_indices` and encodes the sorted
78/// order of the `keys` (the names of the tag types).
79///
80/// "Static" in the name refers to the fact that it takes and returns an array of statically
81/// known size, avoiding `Vec` allocations.
82pub fn static_apply_reordering<T: Copy, const N: usize>(values: &mut [T; N], indices: &[usize; N]) {
83 let tmp_values: [T; N] = *values;
84 for (old_index, new_index) in indices.iter().enumerate() {
85 values[old_index] = tmp_values[*new_index];
86 }
87}
88
89/// Reorder `values` in place according to the sorted order of `keys`.
90///
91/// Both slices must have the same length. "Static" in the name refers to the fact that it
92/// takes and returns an array of statically known size, avoiding `Vec` allocations.
93pub fn static_reorder_by_keys<T: Ord + Copy, U: Copy, const N: usize>(
94 keys: &[T; N],
95 values: &mut [U; N],
96) {
97 let indices: [usize; N] = static_sorted_indices(keys);
98 static_apply_reordering(values, &indices);
99}
100
101/// Reorder `values` according to the sorted order of `keys`.
102///
103/// Both slices are assumed to have the same length. This version works in cases where
104/// the size of the slice is not known at compile time, returning an allocated `Vec`.
105pub fn reorder_by_keys<T: Ord + Copy, U: Copy>(keys: &mut [T], values: &mut [U]) {
106 let indices: Vec<usize> = sorted_indices(keys);
107 let tmp_keys = Vec::from(&*keys);
108 let tmp_values = Vec::from(&*values);
109
110 // Apply the permutation to values
111 for (old_index, new_index) in indices.into_iter().enumerate() {
112 values[old_index] = tmp_values[new_index];
113 keys[old_index] = tmp_keys[new_index];
114 }
115}