superset_map

B-tree-backed map whose keys are distinct supersets.
git clone https://git.philomathiclife.com/repos/superset_map
Log | Files | Refs | README

commit c4f58b7ffcea7d0563649cc1e0368f0c183e9e1b
parent ed95bb90160c4586f3b58d43e6bce2c5ffbbd7c5
Author: Zack Newman <zack@philomathiclife.com>
Date:   Fri,  6 Sep 2024 11:34:52 -0600

update deps. address lints

Diffstat:
MCargo.toml | 18+++---------------
MREADME.md | 58++++++++++++++++++++++++++++++++--------------------------
Msrc/lib.rs | 65++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/superset_map.rs | 137++++++++++++++++++++++++++++++-------------------------------------------------
Msrc/superset_set.rs | 85++++++++++++++++++++++++++++---------------------------------------------------
5 files changed, 153 insertions(+), 210 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,22 +9,10 @@ license = "MIT OR Apache-2.0" name = "superset_map" readme = "README.md" repository = "https://git.philomathiclife.com/repos/superset_map/" -version = "0.2.3" - -[lib] -name = "superset_map" -path = "src/lib.rs" - -[dependencies] -zfc = { version = "0.3.2", default-features = false } - -[dev-dependencies] -num-bigint = { version = "0.4.4", default-features = false } +version = "0.3.0" [badges] maintenance = { status = "actively-developed" } -[profile.release] -lto = true -panic = 'abort' -strip = true +[dependencies] +zfc = { version = "0.4.0", default-features = false } diff --git a/README.md b/README.md @@ -1,19 +1,17 @@ -# `superset_map` - -`superset_map` is a library for [`Set`](https://docs.rs/zfc/latest/zfc/trait.Set.html)s that have an order defined on them. -Its main data structure is [`SupersetMap`](https://docs.rs/superset_map/latest/superset_map/struct.SupersetMap.html) -which is a specialized version of [`BTreeMap`](https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html) -where only supersets are stored. This can be useful when the keys don't fit well or at all with the concept of -[`RangeBounds`](https://doc.rust-lang.org/stable/core/ops/trait.RangeBounds.html). - -## Example - +# `superset_map` + +`superset_map` is a library for `Set`s that have an order defined on them. Its main data structure is +`SupersetMap` which is a specialized version of `BTreeMap` where only supersets are stored. This can be +useful when the keys don't fit well or at all with the concept of `RangeBounds`. + +Nightly Rust is required. Once +[`BTreeMap` cursors are stabilized](https://github.com/rust-lang/rust/issues/107540), stable Rust will work. + +## Example + ```rust -use core::borrow::Borrow; -use core::cmp::Ordering; -use num_bigint::BigUint; -use superset_map::{SetOrd, SupersetSet}; -use zfc::{BoundedCardinality, Cardinality, Set}; +use core::{borrow::Borrow, cmp::Ordering}; +use superset_map::{zfc::{num_bigint::BigUint, BoundedCardinality, Cardinality, Set}, SetOrd, SupersetSet}; #[derive(Clone, Copy, Eq, PartialEq)] struct ShortAscii<'a> { val: &'a [u8], @@ -130,14 +128,22 @@ fn main() { assert!(iter.next().is_none()); } ``` - -### Status - -This package will be actively maintained until it is deemed “feature complete”. - -The crates are only tested on the `x86_64-unknown-linux-gnu` and `x86_64-unknown-openbsd` targets, but -they should work on any [Tier 1 with Host Tools](https://doc.rust-lang.org/beta/rustc/platform-support.html) -target. - -Version `1.69.0` or newer of nightly `rustc` is required. Once -[`BTreeMap` cursors are stabilized](https://github.com/rust-lang/rust/issues/107540), stable `rustc` will work. + +## 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. + +### Status + +The crate is only tested on `x86_64-unknown-linux-gnu` and `x86_64-unknown-openbsd` targets, but it should work +on most platforms. diff --git a/src/lib.rs b/src/lib.rs @@ -1,22 +1,17 @@ //! # `superset_map` //! -//! `superset_map` is a library for [`Set`]s that have an order defined on them. -//! Its main data structure is [`SupersetMap`] which is a specialized version of -//! [`BTreeMap`](https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html) -//! where only supersets are stored. This can be useful when the keys don't fit well or at all with the concept of -//! [`RangeBounds`](https://doc.rust-lang.org/stable/core/ops/trait.RangeBounds.html). +//! `superset_map` is a library for [`Set`]s that have an order defined on them. Its main data structure is +//! [`SupersetMap`] which is a specialized version of [`BTreeMap`] where only supersets are stored. This can be +//! useful when the keys don't fit well or at all with the concept of [`RangeBounds`]. //! -//! Version `1.69.0` or newer of nightly `rustc` is required. Once -//! [`BTreeMap` cursors are stabilized](https://github.com/rust-lang/rust/issues/107540), stable `rustc` will work. +//! Nightly Rust is required. Once +//! [`BTreeMap` cursors are stabilized](https://github.com/rust-lang/rust/issues/107540), stable Rust will work. //! //! ## Example //! -//! ```rust -//! use core::borrow::Borrow; -//! use core::cmp::Ordering; -//! use num_bigint::BigUint; -//! use superset_map::{SetOrd, SupersetSet}; -//! use zfc::{BoundedCardinality, Cardinality, Set}; +//! ``` +//! # use core::{borrow::Borrow, cmp::Ordering}; +//! # use superset_map::{zfc::{num_bigint::BigUint, BoundedCardinality, Cardinality, Set}, SetOrd, SupersetSet}; //! #[derive(Clone, Copy, Eq, PartialEq)] //! struct ShortAscii<'a> { //! val: &'a [u8], @@ -119,27 +114,27 @@ //! } //! } //! impl<'a> SetOrd for WildcardAscii<'a> {} -//! fn main() { -//! let mut set = SupersetSet::new(); -//! set.insert(WildcardAscii::Plain(ShortAscii::new(b"foo").unwrap())); -//! set.insert(WildcardAscii::Plain(ShortAscii::new(b"bar").unwrap())); -//! set.insert(WildcardAscii::Wildcard(ShortAscii::new(b"b").unwrap())); -//! set.insert(WildcardAscii::Wildcard(ShortAscii::new(b"bar").unwrap())); -//! let mut iter = set.into_iter(); -//! assert!(iter.next().map_or(false, |s| s -//! == WildcardAscii::Wildcard(ShortAscii::new(b"b").unwrap()))); -//! assert!(iter.next().map_or(false, |s| s -//! == WildcardAscii::Plain(ShortAscii::new(b"foo").unwrap()))); -//! assert!(iter.next().is_none()); -//! } +//! let mut set = SupersetSet::new(); +//! set.insert(WildcardAscii::Plain(ShortAscii::new(b"foo").unwrap())); +//! set.insert(WildcardAscii::Plain(ShortAscii::new(b"bar").unwrap())); +//! set.insert(WildcardAscii::Wildcard(ShortAscii::new(b"b").unwrap())); +//! set.insert(WildcardAscii::Wildcard(ShortAscii::new(b"bar").unwrap())); +//! let mut iter = set.into_iter(); +//! assert!(iter.next().map_or(false, |s| s +//! == WildcardAscii::Wildcard(ShortAscii::new(b"b").unwrap()))); +//! assert!(iter.next().map_or(false, |s| s +//! == WildcardAscii::Plain(ShortAscii::new(b"foo").unwrap()))); +//! assert!(iter.next().is_none()); //! ``` #![no_std] #![feature(btree_cursors)] #![deny( + unknown_lints, future_incompatible, let_underscore, missing_docs, nonstandard_style, + refining_impl_trait, rust_2018_compatibility, rust_2018_idioms, rust_2021_compatibility, @@ -158,11 +153,23 @@ clippy::style, clippy::suspicious )] -#![allow( +#![expect( clippy::blanket_clippy_restriction_lints, - clippy::needless_doctest_main, - clippy::pub_use + clippy::implicit_return, + clippy::min_ident_chars, + clippy::missing_trait_methods, + clippy::pub_use, + clippy::semicolon_outside_block, + clippy::single_char_lifetime_names, + reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs" )] +#[cfg(doc)] +extern crate alloc; +#[cfg(doc)] +use alloc::collections::BTreeMap; +#[cfg(doc)] +use core::ops::RangeBounds; +pub use zfc; use zfc::Set; /// A map of keys and values where the keys implement [`SetOrd`]. The map is backed by a B-tree. mod superset_map; diff --git a/src/superset_map.rs b/src/superset_map.rs @@ -1,50 +1,22 @@ -#![deny( - unsafe_code, - unused, - warnings, - clippy::all, - clippy::cargo, - clippy::complexity, - clippy::correctness, - clippy::nursery, - clippy::pedantic, - clippy::perf, - clippy::restriction, - clippy::style, - clippy::suspicious -)] -#![allow( - clippy::blanket_clippy_restriction_lints, - clippy::implicit_return, - clippy::min_ident_chars, - clippy::missing_trait_methods, - clippy::semicolon_outside_block, - clippy::single_char_lifetime_names -)] extern crate alloc; use crate::SetOrd; use alloc::collections::btree_map::{ BTreeMap, CursorMut, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, OccupiedEntry, Range, RangeMut, Values, ValuesMut, }; -use core::borrow::Borrow; -use core::ops::{Bound, Index, RangeBounds}; +use core::{ + borrow::Borrow, + ops::{Bound, Index, RangeBounds}, +}; /// A minimal collection of `(K, V)`s. -/// Internally it is based on a [`BTreeMap`]. -/// When a `(K, V)` is [`SupersetMap::insert`]ed, -/// it won't actually be inserted unless -/// there isn't a `K` already in -/// the map that is a superset of it. -/// In such event, all `K`s that -/// are subsets of the to-be-inserted `K` -/// are removed before inserting the `K`. -/// Note this can have quite good performance due to the -/// fact that a single search is necessary to detect -/// if insertion should occur; furthermore since all -/// subsets occur immediately before where the key -/// will be inserted, a simple linear scan is sufficient -/// to remove subsets avoiding the need to search -/// the entire map. +/// +/// Internally it is based on a [`BTreeMap`]. When a `(K, V)` is [`SupersetMap::insert`]ed, it won't actually be +/// inserted unless there isn't a `K` already in the map that is a superset of it. In such event, all `K`s that +/// are subsets of the to-be-inserted `K` are removed before inserting the `K`. +/// +/// Note this can have quite good performance due to the fact that a single search is necessary to detect if +/// insertion should occur; furthermore since all subsets occur immediately before where the key will be inserted, +/// a simple linear scan is sufficient to remove subsets avoiding the need to search the entire map. #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SupersetMap<K, V> { /// Collection of `(K, V)`s. @@ -399,8 +371,11 @@ where /// In the event `(key, value)` will be inserted, all `(K, V)`s /// where the `K` is a subset of `key` are first removed before /// inserting. + #[expect( + unsafe_code, + reason = "we rely on CursorMut::insert_before_unchecked since we verify it is safe to do so" + )] #[inline] - #[allow(unsafe_code)] pub fn insert(&mut self, key: K, value: V) -> bool { // Since the only way to `insert` a `(K, V)` is via this function, // the following is true: @@ -521,15 +496,13 @@ where None } /// Removes all proper subsets of key from the map, returning the count removed. - /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match the ordering on the key type. - /// # Overflow Behavior - /// - /// The method does no guarding against overflows, so the removal of more than `usize::MAX` elements either produces the wrong result or panics. If debug assertions are enabled, a panic is guaranteed. - /// # Panics - /// - /// This function might panic if the number of elements removed is greater than `usize::MAX`. - #[inline] - #[allow(clippy::arithmetic_side_effects)] + /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match + /// the ordering on the key type. + #[expect( + clippy::arithmetic_side_effects, + reason = "we count how many items are removed which can never exceed the length" + )] + #[inline] pub fn remove_proper_subsets<Q>(&mut self, key: &Q) -> usize where K: Borrow<Q>, @@ -548,15 +521,13 @@ where count } /// Removes all proper supersets of key from the map, returning the count removed. - /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match the ordering on the key type. - /// # Overflow Behavior - /// - /// The method does no guarding against overflows, so the removal of more than `usize::MAX` elements either produces the wrong result or panics. If debug assertions are enabled, a panic is guaranteed. - /// # Panics - /// - /// This function might panic if the number of elements removed is greater than `usize::MAX`. - #[inline] - #[allow(clippy::arithmetic_side_effects)] + /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match + /// the ordering on the key type. + #[expect( + clippy::arithmetic_side_effects, + reason = "we count how many items are removed which can never exceed the length" + )] + #[inline] pub fn remove_proper_supersets<Q>(&mut self, key: &Q) -> usize where K: Borrow<Q>, @@ -575,15 +546,13 @@ where count } /// Removes all subsets of key from the map, returning the count removed. - /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match the ordering on the key type. - /// # Overflow Behavior - /// - /// The method does no guarding against overflows, so the removal of more than `usize::MAX` elements either produces the wrong result or panics. If debug assertions are enabled, a panic is guaranteed. - /// # Panics - /// - /// This function might panic if the number of elements removed is greater than `usize::MAX`. - #[inline] - #[allow(clippy::arithmetic_side_effects)] + /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match + /// the ordering on the key type. + #[expect( + clippy::arithmetic_side_effects, + reason = "we count how many items are removed which can never exceed the length" + )] + #[inline] pub fn remove_subsets<Q>(&mut self, key: &Q) -> usize where K: Borrow<Q>, @@ -613,15 +582,13 @@ where count } /// Removes all supersets of key from the map, returning the count removed. - /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match the ordering on the key type. - /// # Overflow Behavior - /// - /// The method does no guarding against overflows, so the removal of more than `usize::MAX` elements either produces the wrong result or panics. If debug assertions are enabled, a panic is guaranteed. - /// # Panics - /// - /// This function might panic if the number of elements removed is greater than `usize::MAX`. - #[inline] - #[allow(clippy::arithmetic_side_effects)] + /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must + /// match the ordering on the key type. + #[expect( + clippy::arithmetic_side_effects, + reason = "we count how many items are removed which can never exceed the length" + )] + #[inline] pub fn remove_supersets<Q>(&mut self, key: &Q) -> usize where K: Borrow<Q>, @@ -629,16 +596,17 @@ where { let mut cursor = self.map.lower_bound_mut(Bound::Included(key)); let mut count = 0; - // To avoid unnecessary checks for equivalence, - // we separately call `is_superset` on the first element + // To avoid unnecessary checks for equivalence, // we separately call `is_superset` on the first element // while calling `is_proper_superset` on the other elements. if let Some(ge) = cursor.next() { if ge.0.borrow().is_superset(key) { cursor.remove_prev(); + // `count` never exceeds `self.map.len()`; thus overflow is no worry. count += 1; while let Some(gt) = cursor.next() { if gt.0.borrow().is_proper_superset(key) { cursor.remove_prev(); + // `count` never exceeds `self.map.len()`; thus overflow is no worry. count += 1; } else { return count; @@ -742,12 +710,11 @@ impl<'a, K, V> IntoIterator for &'a mut SupersetMap<K, V> { } #[cfg(test)] mod tests { - use super::SupersetMap; - use crate::SetOrd; - use core::borrow::Borrow; - use core::cmp::Ordering; - use num_bigint::BigUint; - use zfc::{BoundedCardinality, Cardinality, Set}; + use super::{ + super::zfc::{num_bigint::BigUint, BoundedCardinality, Cardinality, Set}, + SetOrd, SupersetMap, + }; + use core::{borrow::Borrow, cmp::Ordering}; #[derive(Eq, PartialEq)] struct ClosedInterval { min: usize, diff --git a/src/superset_set.rs b/src/superset_set.rs @@ -1,36 +1,15 @@ -#![deny( - unsafe_code, - unused, - warnings, - clippy::all, - clippy::cargo, - clippy::complexity, - clippy::correctness, - clippy::nursery, - clippy::pedantic, - clippy::perf, - clippy::restriction, - clippy::style, - clippy::suspicious -)] -#![allow( - clippy::blanket_clippy_restriction_lints, - clippy::implicit_return, - clippy::min_ident_chars, - clippy::missing_trait_methods, - clippy::same_name_method, - clippy::semicolon_outside_block, - clippy::shadow_unrelated, - clippy::single_char_lifetime_names -)] extern crate alloc; -use crate::{SetOrd, SupersetMap}; +use crate::{ + zfc::{BoundedCardinality, Cardinality, Set}, + SetOrd, SupersetMap, +}; use alloc::collections::btree_map::{self, IntoKeys, Keys}; -use core::borrow::Borrow; -use core::hash::{Hash, Hasher}; -use core::iter::FusedIterator; -use core::ops::{BitAnd, BitOr, Bound, RangeBounds}; -use zfc::{BoundedCardinality, Cardinality, Set}; +use core::{ + borrow::Borrow, + hash::{Hash, Hasher}, + iter::FusedIterator, + ops::{BitAnd, BitOr, Bound, RangeBounds}, +}; /// A lazy iterator producing elements in the intersection of `SupersetSet`s. /// /// This `struct` is created by [`SupersetSet::intersection`]. @@ -52,7 +31,6 @@ where T: SetOrd, { type Item = &'a T; - #[allow(clippy::else_if_without_else)] #[inline] fn next(&mut self) -> Option<Self::Item> { loop { @@ -66,6 +44,8 @@ where } } else if cur2.is_proper_subset(prev1) { return Some(cur2); + } else { + // Do nothing. } } else { self.prev_1 = None; @@ -81,6 +61,8 @@ where } } else if cur1.is_proper_subset(prev2) { return Some(cur1); + } else { + // Do nothing. } } else { self.prev_1 = None; @@ -96,6 +78,8 @@ where } else if cur2.is_proper_subset(cur1) { self.prev_1 = Some(cur1); return Some(cur2); + } else { + // Do nothing. } } else { return None; @@ -208,21 +192,14 @@ impl<'a, T> Iterator for Range<'a, T> { } } /// A minimal collection of `T`s. -/// Internally it is based on a [`SupersetMap`]. -/// When a `T` is [`SupersetSet::insert`]ed, -/// it won't actually be inserted unless -/// there isn't a `T` already in -/// the set that is a superset of it. -/// In such event, all `T`s that -/// are subsets of the to-be-inserted `T` -/// are removed before inserting the `T`. -/// Note this can have quite good performance due to the -/// fact that a single search is necessary to detect -/// if insertion should occur; furthermore since all -/// subsets occur immediately before where the value -/// will be inserted, a simple linear scan is sufficient -/// to remove subsets avoiding the need to search -/// the entire set. +/// +/// Internally it is based on a [`SupersetMap`]. When a `T` is [`SupersetSet::insert`]ed, it won't actually be +/// inserted unless there isn't a `T` already in the set that is a superset of it. In such event, all `T`s that +/// are subsets of the to-be-inserted `T` are removed before inserting the `T`. +/// +/// Note this can have quite good performance due to the fact that a single search is necessary to detect if +/// insertion should occur; furthermore since all subsets occur immediately before where the value will be inserted, +/// a simple linear scan is sufficient to remove subsets avoiding the need to search the entire set. #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct SupersetSet<T> { /// Collection of `T`s. @@ -269,6 +246,7 @@ where T: Ord, { /// Read [`BTreeSet::contains`](https://doc.rust-lang.org/alloc/collections/btree_set/struct.BTreeSet.html#method.contains). + #[expect(clippy::same_name_method, reason = "consistent with BTreeSet")] #[inline] pub fn contains<Q>(&self, value: &Q) -> bool where @@ -582,7 +560,6 @@ where } /// Adds a value to the set iff there doesn't already exist a proper superset of it. /// In the event a value that is equal to it already exists, then it is replaced and returned. - #[allow(clippy::option_if_let_else)] #[inline] pub fn replace(&mut self, value: T) -> Option<T> { let prev = { @@ -766,7 +743,6 @@ where T: SetOrd, { type Item = &'a T; - #[allow(clippy::else_if_without_else)] #[inline] fn next(&mut self) -> Option<Self::Item> { loop { @@ -826,12 +802,11 @@ where } #[cfg(test)] mod tests { - use super::SupersetSet; - use crate::SetOrd; - use core::borrow::Borrow; - use core::cmp::Ordering; - use num_bigint::BigUint; - use zfc::{BoundedCardinality, Cardinality, Set}; + use super::{ + super::zfc::{num_bigint::BigUint, BoundedCardinality, Cardinality, Set}, + SetOrd, SupersetSet, + }; + use core::{borrow::Borrow, cmp::Ordering}; #[derive(Eq, PartialEq)] struct ClosedInterval { min: usize,