Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/rustonaut/total-order-multi-map

A multimap with at the same time keeps the total insertion ordering of all elements
https://github.com/rustonaut/total-order-multi-map

Last synced: about 2 months ago
JSON representation

A multimap with at the same time keeps the total insertion ordering of all elements

Awesome Lists containing this project

README

        

total-order-multi-map [![Crates.io](https://img.shields.io/crates/v/total-order-multi-map.svg)](https://crates.io/crates/total-order-multi-map) [![total-order-multi-map](https://docs.rs/total-order-multi-map/badge.svg)](https://docs.rs/total-order-multi-map) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
=============

A multi-map with at the same time keeps the total insertion ordering of all elements,
this means if you insert following key-value pairs: `(K1, V1)`, `(K2, V2)`, `(K1, V3)`
It will remember that the insertion oder was exact this (`V1`, `V2`, `V3`) i.e.
it won't collapse the insertion order on a per-key basis.

At the same time it should have similar performance for accessing entries as a normal
`HashMap` (else we could have just created a `Vec`).

It does so by limiting its values to such which implement `StableDeref`, e.g.
`Box` through which it can have more than one ~reference~ pointer to
a value. While it uses unsafety internally the provided interface is safe.
It also makes sure to be unwind + resume safe.

Example
--------

```rust
extern crate total_order_multi_map as tomm;

use std::fmt::{self, Display};
use tomm::TotalOrderMultiMap;

/// for now the key has to be copy
type Key = &'static str;

/// the map is made for thinks which dereference
/// e.g. trait objects, through e.g. `String` or
/// `Rc` would be fine, too.
type Value = Box;

fn main() {
let mut map = TotalOrderMultiMap::::new();

map.add("key1", mk_box_str("val1"));
map.add("key1", mk_my_thingy("val2"));
map.add("key2", mk_box_str("val3"));
map.add("key1", mk_my_thingy("val4"));
map.add("key0", mk_box_str("val5"));

let stringed = map
.iter()
// val is of type `&Display`
.map(|(key, val)| format!("{}:{}", key, val))
.collect::>();

// as it can be seen the total insertion order was kept
assert_eq!(stringed, &[
"key1:val1".to_owned(),
"key1:val2".to_owned(),
"key2:val3".to_owned(),
"key1:val4".to_owned(),
"key0:val5".to_owned()
]);

// It's also still a multi map.
// Note that the get operation has roughly the same
// performance as in a hash map (through you then
// iterate over the elements associated with the key
// roughly with the speed of iterating over an `Vec`).
{
let values = map.get("key1").expect("key1 is in the map");
let stringed = values
.map(|val| format!("{}", val))
.collect::>();

assert_eq!(stringed, &[ "val1", "val2", "val4" ]);
}

// but as we have an order we can remove
// "the last inserted" element
let (key, val) = map.pop().unwrap();
assert_eq!(format!("{}:{}", key, val), "key0:val5");

// or remove all for an specific key as it's
// an multi map
map.remove_all("key1");
assert!(map.get("key1").is_none());

println!("DONE (I only contain asserts)")
}

//---- some function to create dummy values for the example ----

fn mk_box_str(inp: &str) -> Box {
// sadly we can't cast Box to Box
// (you would need non static vtables for this...)
Box::new(inp.to_owned())
}

#[derive(Debug)]
struct MyThingy { val: String }

impl Display for MyThingy {
fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
write!(fter, "{}", self.val)
}
}

fn mk_my_thingy(inp: &str) -> Box {
Box::new(MyThingy { val: inp.to_owned() })
}
```

Missing Parts
-------------

Following thinks can be implemented and should
be added to further versions:

- benchmarks
- use `SmallVec` for the multi map
- allow `&mut` access to `V::Target` if `V: StableDeref + DerefMut`
- indexed access
- indexed get off key-value pairs
- indexed remove
- indexed get _in_ the return value of `get`, i.e. a multi map group
- improved entry api
- allow access to other values for same key
- potentially turn the result of entry + insert into the result of get + unwrap
- improved grouped iteration, maybe??
- the current implementation groups by key but doesn't indicate
when it switches to the next value
- allow non-copy keys??
- we still don't want to duplicate/clone keys so we would have to do
some trickery there

License
-------

Licensed under either of

- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

Contribution
------------

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.

Change Log
-----------

- `v0.3`:
- `TotalOrderMultiMap.retain` now accepts a predicate accepting `&V::Target` instead
of `&V`
- `v0.4.1`:
- flatten return value to `get` to return empty iterators instead
of `None`
- requires `DerefMut` instead of just `Deref`
- has mutable access methods like `get_mut`
- split `insert` into `add` and `set` where `set` replaces any value
associated with the key prev. with the new value returning the old
value
- `v0.4.2`:
- mut iter to non mut iter conversions
- Iter from IterMut
- Values from ValuesMut
- EntryValues from EntryValuesMut
- `v0.4.3`:
- Added missing re-exports. Values, Values,
GroupedValuesMut are now public as they
should have been from the beginning.
- `v0.4.4`:
- Added `truncate` method allowing the removal
of any think inserted after the first `size`
elements
- `v0.4.5`:
- For entries added `values`,`values_mut` methods to
iterate over the values associated with the key used
to create the entry.