map_of_indexes/lib.rs
1//! # Map-of-indexes
2//!
3//! [](https://crates.io/crates/map-of-indexes)
4//! [](https://docs.rs/map-of-indexes)
5//!
6//! A small utility crate when you have a list of unique but not dense indexes for which to each you want to associates a value.
7//! In the documentation the indexes are referred as `key`. *Not* an indexed map!
8//!
9//! It can be considered a slower but more compact version of [`BTreeMap`](std::collections::BTreeMap).
10//! # Examples
11//! A brief example of the crate's capacities
12//! ```
13//! use map_of_indexes::{MapOfIndexes, MapOfIndexesError, KeyValue};
14//!
15//! # fn main() -> Result<(), MapOfIndexesError> {
16//! let v = vec![(3, 4), (1, 2), (5, 6)];
17//! let mut map: MapOfIndexes::<(u8, u16)> = v.try_into()?;
18//!
19//! map.push((7,8));
20//! let push_res = map.push_checked((0,9));
21//! assert_eq!(push_res, Err(MapOfIndexesError::SmallerKey));
22//!
23//! let old_key_value = map.set((1,9))?;
24//! assert_eq!(old_key_value.key(), &1);
25//! assert_eq!(old_key_value.value(), &2);
26//! # Ok(())
27//! # }
28//! ```
29//! [`CombinedKeyValue`](crate::CombinedKeyValue) is a compact representation when you need to save space.
30//! ```
31//! # use map_of_indexes::{MapOfIndexes, MapOfIndexesError, KeyValue};
32//! use map_of_indexes::{CombinedKeyValue};
33//! # fn main() -> Result<(), MapOfIndexesError> {
34//! // We have keys that take up to 40 bits, and value up to 24;
35//! // Using (u64, u64) would have wasted 8 byte per entry.
36//! type CombinedU64 = CombinedKeyValue<u64, 40, 24>;
37//! CombinedU64::safety_check(); // ensure that key and value size fit on the unsigned integer.
38//!
39//! let v = vec![CombinedU64::new(3u64, 4u32), CombinedU64::new(1u64, 2u32), CombinedU64::new(5u64, 6u32)];
40//! let map: MapOfIndexes<_> = v.try_into()?;
41//! let inner_raw: Vec<u64> = Vec::from_iter(map.iter().map(|x| *x.as_ref()));
42//! assert_eq!(inner_raw, vec![2199023255553, 4398046511107, 6597069766661]);
43//! # Ok(())
44//! # }
45//! ```
46//! For an even more compact representation, consider using the [`bitvec`](https://docs.rs/bitvec/latest/bitvec/index.html) crate.
47
48#![warn(clippy::pedantic)]
49#![warn(clippy::cargo)]
50#![allow(clippy::semicolon_if_nothing_returned)]
51// #![allow(clippy::missing_panics_doc)]
52// #![allow(clippy::missing_errors_doc)]
53
54use std::fmt::Debug;
55use std::ops::Deref;
56
57use thiserror::Error;
58
59/// Trait that `T` must implement to be able to work with [`MapOfIndexes`](crate::MapOfIndexes) of `T`.
60///
61/// Can be implemented on your own custom structs. `KeyValue::K` represents the key (index) of the pair, while `KeyValue::V` is the value.
62///
63/// [`KeyValue`](crate::KeyValue) is already implemented for all 2-tuples, the first element being considered as the key.
64/// # Examples
65/// ```
66/// use map_of_indexes::KeyValue;
67///
68/// let tuple: (String, i64) = ("Key".to_string(), 123);
69/// // In this blanket implementation, both functions return a reference
70/// assert_eq!(tuple.key(), &"Key");
71/// assert_eq!(tuple.value(), &123i64);
72/// ```
73///
74/// If you need a very compact representation, see [`CombinedKeyValue`](crate::CombinedKeyValue), which stores both the key and the key on a uint of a given size.
75// #[doc(notable_trait)]
76pub trait KeyValue<'a> {
77 type K: Ord;
78 type V;
79 fn key(&'a self) -> Self::K;
80 fn value(&'a self) -> Self::V;
81}
82
83impl<'a, KEY: Ord, VALUE> KeyValue<'a> for (KEY, VALUE)
84where
85 KEY: 'a,
86 VALUE: 'a,
87{
88 type K = &'a KEY;
89 type V = &'a VALUE;
90 fn key(&'a self) -> Self::K {
91 &self.0
92 }
93 fn value(&'a self) -> Self::V {
94 &self.1
95 }
96}
97
98/// Different kind of errors generated by the library
99#[derive(Error, Debug, PartialEq, Eq, Hash)]
100pub enum MapOfIndexesError {
101 /// At least two elements with same keys. Keys must be uniques.
102 #[error("At least two elements with same keys. Keys must be uniques.")]
103 DuplicateKeys,
104 /// No elements were found with this key.
105 #[error("No elements were found with this key.")]
106 KeyNotFound,
107 /// Attempted to push an element with a lower key than last element.
108 #[error("Attempted to push an element with a lower key than last element.")]
109 SmallerKey,
110}
111
112/// Basically a sorted vec
113#[derive(Clone, Debug)]
114pub struct MapOfIndexes<T> {
115 inner: Vec<T>,
116}
117
118/// Easy access to the underlying vec
119impl<T> Deref for MapOfIndexes<T> {
120 type Target = Vec<T>;
121
122 fn deref(&self) -> &Self::Target {
123 &self.inner
124 }
125}
126
127impl<T: for<'a> KeyValue<'a>> Default for MapOfIndexes<T> {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133/// Under the hood, will sort the vec by key. Will succeed if there are no elements with the same key
134impl<T: for<'a> KeyValue<'a>> TryFrom<Vec<T>> for MapOfIndexes<T> {
135 type Error = MapOfIndexesError;
136
137 fn try_from(mut vec: Vec<T>) -> Result<Self, Self::Error> {
138 // Other solution was to check duplicate while sorting, supposedly faster to make linear search after
139 // when comparing elements is cheap
140 vec.sort_unstable_by(|a, b| a.key().cmp(&b.key()));
141 let duplicate = vec.windows(2).any(|w| w[0].key() == w[1].key());
142 if duplicate {
143 Err(MapOfIndexesError::DuplicateKeys)
144 } else {
145 Ok(Self { inner: vec })
146 }
147 }
148}
149
150impl<T: for<'a> KeyValue<'a>> MapOfIndexes<T> {
151 #[must_use]
152 pub fn new() -> Self {
153 Self { inner: Vec::new() }
154 }
155
156 #[must_use]
157 pub fn with_capacity(capacity: usize) -> Self {
158 Self {
159 inner: Vec::with_capacity(capacity),
160 }
161 }
162
163 /// Push an element to the map. Panic if the pushed key is smaller than the last element's one.
164 /// # Panics
165 /// ```should_panic
166 /// use map_of_indexes::MapOfIndexes;
167 ///
168 /// let mut m: MapOfIndexes<(isize, &'static str)> = MapOfIndexes::new();
169 /// m.push((1, "cool"));
170 /// m.push((-10, "panic!"));
171 /// ```
172 #[inline]
173 pub fn push(&mut self, element: T) {
174 match self.push_checked(element) {
175 Ok(()) => (),
176 Err(err) => panic!("{}", err),
177 }
178 }
179
180 /// Push an element to the map.
181 /// # Example
182 /// ```
183 /// use map_of_indexes::{MapOfIndexes, MapOfIndexesError};
184 ///
185 /// let mut m: MapOfIndexes<(isize, &'static str)> = MapOfIndexes::new();
186 /// assert!(m.push_checked((1, "cool")).is_ok())
187 /// ```
188 /// # Errors
189 /// Returns [`MapOfIndexesError::SmallerKey`](crate::MapOfIndexesError) if the pushed key is smaller than the last element's one.
190 /// ```
191 /// # use map_of_indexes::{MapOfIndexes, MapOfIndexesError};
192 ///
193 /// let mut m: MapOfIndexes<(isize, &'static str)> = MapOfIndexes::new();
194 /// m.push((1, "cool"));
195 /// let err = m.push_checked((-10, "err!"));
196 /// assert_eq!(err, Err(MapOfIndexesError::SmallerKey))
197 /// ```
198 pub fn push_checked(&mut self, element: T) -> Result<(), MapOfIndexesError> {
199 if let Some(last) = self.last() {
200 if last.key() >= element.key() {
201 return Err(MapOfIndexesError::SmallerKey);
202 }
203 }
204 self.inner.push(element);
205 Ok(())
206 }
207
208 #[inline]
209 fn get_idx<'a>(&'a self, key: &<T as KeyValue<'a>>::K) -> Option<usize> {
210 self.binary_search_by(|t| t.key().cmp(key)).ok()
211 }
212
213 /// Performs a dichotomal search and returns the element
214 pub fn get_element<'a>(&'a self, key: &<T as KeyValue<'a>>::K) -> Option<&T> {
215 self.get_idx(key).map(|idx| &self[idx])
216 }
217
218 /// Performs a dichotomal search and returns the value
219 pub fn get_value<'a>(&'a self, key: &<T as KeyValue<'a>>::K) -> Option<<T as KeyValue<'_>>::V> {
220 self.get_idx(key).map(|idx| self[idx].value())
221 }
222
223 /// Find and replace the key-value element, returning the previous key-value if found, or an error otherwise.
224 /// # Examples
225 /// ```
226 /// # use map_of_indexes::MapOfIndexes;
227 /// let mut s = MapOfIndexes::<(u16, u16)>::new();
228 /// s.push((10, 10));
229 /// s.push((11, 11));
230 /// s.push((12, 12));
231 /// s.push((13, 13));
232 /// let old_key_value = s.set((10, 100)).unwrap();
233 /// assert_eq!(old_key_value, (10, 10));
234 /// assert_eq!(*s, &[(10, 100), (11, 11), (12, 12), (13, 13)]);
235 /// ```
236 /// # Errors
237 /// ```
238 /// use map_of_indexes::{MapOfIndexes, MapOfIndexesError};
239 ///
240 /// let mut s = MapOfIndexes::<(&'static str, &'static str)>::new();
241 /// s.push(("test", "value"));
242 /// let err = s
243 /// .set(("key does not exist", "err must be returned"))
244 /// .err()
245 /// .unwrap();
246 /// assert_eq!(err, MapOfIndexesError::KeyNotFound);
247 /// ```
248 pub fn set(&mut self, element: T) -> Result<T, MapOfIndexesError> {
249 let idx_opt = self.get_idx(&element.key());
250 idx_opt
251 .map(|idx| std::mem::replace(&mut self.inner[idx], element))
252 .ok_or(MapOfIndexesError::KeyNotFound)
253 }
254}
255
256/// A compact struct that implement [`KeyValue`](crate::KeyValue)
257///
258/// Both the key and value are stored on the same element which can be any unsigned integer.
259///
260/// # Examples
261///
262/// ```
263/// # use map_of_indexes::{MapOfIndexes, MapOfIndexesError, KeyValue};
264/// use map_of_indexes::{CombinedKeyValue};
265/// # fn main() -> Result<(), MapOfIndexesError> {
266/// // We have keys that take up to 40 bits, and value up to 24;
267/// // Using (u64, u64) would have wasted 8 byte per entry.
268/// type CombinedU64 = CombinedKeyValue<u64, 40, 24>;
269/// CombinedU64::safety_check(); // ensure that key and value size fit on the unsigned integer.
270///
271/// let v = vec![CombinedU64::new(3u64, 4u32), CombinedU64::new(1u64, 2u32), CombinedU64::new(5u64, 6u32)];
272/// let map: MapOfIndexes<_> = v.try_into()?;
273/// let inner_raw: Vec<u64> = Vec::from_iter(map.iter().map(|x| *x.as_ref()));
274/// assert_eq!(inner_raw, vec![2199023255553, 4398046511107, 6597069766661]);
275/// # Ok(())
276/// # }
277/// ```
278///
279/// Not working with signed integer
280/// ```compile_fail
281/// use map_of_indexes::CombinedKeyValue;
282///
283/// // Will not be able to be instanciated
284/// type CombinedI8 = CombinedKeyValue<i8, 4, 4>;
285/// let combined = CombinedI8::new(-10, 3);
286/// ```
287#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
288pub struct CombinedKeyValue<T, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8>(T);
289
290// TODO, make that work
291// impl<'a, T, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8> Debug for CombinedKeyValue<T, KEY_NB_BITS, VALUE_NB_BITS> where CombinedKeyValue<T, KEY_NB_BITS, VALUE_NB_BITS>: KeyValue<'a> + Debug{
292// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
293// write!(f, "CombinedKeyValue<{},{},{}> {{ key: {:?}, value: {:?} }}", std::any::type_name::<T>(), KEY_NB_BITS, VALUE_NB_BITS, self.key(), self.value())
294// }
295// }
296
297/// Easy access to the underlying `T`
298impl<T, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8> AsRef<T>
299 for CombinedKeyValue<T, KEY_NB_BITS, VALUE_NB_BITS>
300{
301 fn as_ref(&self) -> &T {
302 &self.0
303 }
304}
305
306// If `KEY_NB_BITS` and `VALUE_NB_BITS` are compatible with backed type, `TryFrom<u128>` should never fail.
307impl<T: TryFrom<u128> + Into<u128>, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8>
308 CombinedKeyValue<T, { KEY_NB_BITS }, { VALUE_NB_BITS }>
309where
310 <T as TryFrom<u128>>::Error: Debug,
311{
312 // To be run once after defining a type alias.
313 // TODO use Macro instead(?) or associated constant?
314 // pub const TEST: usize = std::mem::size_of::<T>() * 8 - (KEY_NB_BITS + VALUE_NB_BITS) as usize;
315
316 /// Check that chosen values `VALUE_NB_BITS` and `KEY_NB_BITS` can fit on `T`
317 /// # Panics
318 /// Panic when this is not the case
319 /// ```should_panic
320 /// use map_of_indexes::CombinedKeyValue;
321 ///
322 /// type CombinedU8 = CombinedKeyValue<u8, 3, 6>;
323 /// CombinedU8::safety_check();
324 /// ```
325 pub fn safety_check() {
326 assert!(
327 !(std::mem::size_of::<T>() * 8 < (KEY_NB_BITS + VALUE_NB_BITS).into()),
328 "KEY_NB_BITS + VALUE_NB_BITS value is higher than the number of bits of the backup type."
329 );
330 }
331
332 /// # Panics
333 /// panics if `value` or `value` has more bits than their declared size
334 /// ```should_panic
335 /// # use map_of_indexes::CombinedKeyValue;
336 /// type CombinedU8 = CombinedKeyValue<u8, 4, 4>;
337 /// CombinedU8::safety_check(); // always
338 ///
339 /// CombinedU8::new(2u8, 16u8); // 2 ** 4 = 16;
340 /// ```
341 pub fn new<K: Into<u128>, V: Into<u128>>(key: K, value: V) -> Self {
342 let key_uint: u128 = key.into();
343 let value_uint: u128 = value.into();
344 assert!(key_uint < 2u128.pow(KEY_NB_BITS.into()));
345 assert!(value_uint < 2u128.pow(VALUE_NB_BITS.into()));
346 Self(
347 T::try_from(key_uint | (value_uint << KEY_NB_BITS))
348 .expect("Run `Self::safety_check` and should never panic"),
349 )
350 }
351}
352
353impl<'a, T: TryFrom<u128> + Ord + Copy, const KEY_NB_BITS: u8, const VALUE_NB_BITS: u8> KeyValue<'a>
354 for CombinedKeyValue<T, { KEY_NB_BITS }, { VALUE_NB_BITS }>
355where
356 u128: From<T>,
357 <T as TryFrom<u128>>::Error: Debug,
358{
359 type K = T;
360 type V = T;
361 fn key(&self) -> Self::K {
362 T::try_from(u128::from(self.0) & (u128::MAX >> (u128::BITS - u32::from(KEY_NB_BITS))))
363 .expect("Run `Self::safety_check` and should never panic")
364 }
365 fn value(&self) -> Self::V {
366 T::try_from(u128::from(self.0) >> KEY_NB_BITS)
367 .expect("Run `Self::safety_check` and should never panic")
368 }
369}
370
371#[cfg(test)]
372mod test {
373 use super::*;
374
375 #[test]
376 fn test_push_sorted() {
377 let mut s = MapOfIndexes::<(i128, u8)>::new();
378 s.push((1, 1));
379 s.push((2, 1));
380 assert_eq!(&s.inner, &[(1, 1), (2, 1)]);
381 }
382
383 #[test]
384 fn test_get_1() {
385 let mut s = MapOfIndexes::<(i128, u8)>::new();
386 s.push((10, 10));
387 s.push((11, 11));
388 s.push((12, 12));
389 s.push((13, 13));
390 for i in 10..14 {
391 assert_eq!(s.get_value(&&i128::from(i)), Some(&(i as u8)));
392 }
393 }
394 #[test]
395 fn test_get_2() {
396 let mut s = MapOfIndexes::<(u8, u8)>::new();
397 for i in 0..u8::MAX {
398 s.push((i, i));
399 assert_eq!(s.get_value(&&i), Some(&i));
400 }
401 }
402
403 #[test]
404 fn test_try_from() {
405 let s: Vec<(i32, u64)> = vec![(1, 1), (-100, 2), (3, 15)];
406 let sorted_map: MapOfIndexes<(i32, u64)> = s.try_into().unwrap();
407 assert_eq!(&sorted_map.inner, &[(-100, 2), (1, 1), (3, 15)])
408 }
409
410 #[test]
411 fn test_try_from_fail_duplicate() {
412 let s: Vec<(i32, u64)> = vec![(1, 1), (1, 2), (3, 15)];
413 let sorted_map_err = MapOfIndexes::<(i32, u64)>::try_from(s).err().unwrap();
414 assert_eq!(sorted_map_err, MapOfIndexesError::DuplicateKeys)
415 }
416
417 #[test]
418 fn test_combined_key_value_type() {
419 type CombinedU8 = CombinedKeyValue<u8, 4, 4>;
420 CombinedU8::safety_check();
421 }
422
423 #[test]
424 #[should_panic]
425 fn test_combined_key_value_type_error_key() {
426 type CombinedU8 = CombinedKeyValue<u8, 5, 4>;
427 CombinedU8::safety_check();
428 }
429
430 #[test]
431 #[should_panic]
432 fn test_combined_key_value_type_error_value() {
433 type CombinedU8 = CombinedKeyValue<u8, 4, 5>;
434 CombinedU8::safety_check();
435 }
436
437 #[test]
438 fn test_combined_key_value_new() {
439 type CombinedU8 = CombinedKeyValue<u8, 4, 4>;
440 CombinedU8::safety_check();
441 let x = CombinedU8::new(4u8, 3u8);
442 assert_eq!(x.key(), 4u8);
443 assert_eq!(x.value(), 3u8);
444 }
445
446 #[test]
447 fn test_get_element_on_large_map() {
448 let mut map = MapOfIndexes::<(u32, u64)>::with_capacity(400000);
449 for i in 0..400000 {
450 map.push((i as u32, i as u64))
451 }
452 println!("{:?}", &map[0..10]);
453 for j in 0..400000 {
454 assert_eq!(map.get_element(&&(j as u32)), Some(&(j as u32, j as u64)))
455 }
456 }
457}