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}