From d7230e99eca666cfb76be2e1231219e86859499e Mon Sep 17 00:00:00 2001 From: Adrien Champion Date: Wed, 1 Apr 2026 09:37:38 +0200 Subject: [PATCH 1/6] chore: version bump, update authors/contributors --- Cargo.toml | 17 ++++++++++------- README.md | 7 ++++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a8cb41..d406671 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,21 @@ [package] name = "hashconsing" -version = "1.6.0" -authors = ["Adrien Champion "] +version = "1.7.0" +authors = [ + "Adrien Champion ", + "Leni Aniva ", +] description = "A hash consing library." documentation = "https://docs.rs/hashconsing" homepage = "https://github.com/AdrienChampion/hashconsing" repository = "https://github.com/AdrienChampion/hashconsing" readme = "README.md" categories = [ - "caching", - "compression", - "concurrency", - "data-structures", - "memory-management", + "caching", + "compression", + "concurrency", + "data-structures", + "memory-management", ] keywords = ["hashconsing", "hash", "consing", "sharing", "caching"] license = "MIT/Apache-2.0" diff --git a/README.md b/README.md index bfce847..de1737d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,12 @@ For more details see [the documentation][doc]. MIT/Apache-2.0 -# Contributors +# Developers + +- [@adrienchampion](https://github.com/adrienchampion), original author +- [@lenianiva](https://github.com/lenianiva), maintainer + +## Contributors - [@alex-ozdemir](https://github.com/alex-ozdemir) From 7534301847b330e50110b65df851d960402d71be Mon Sep 17 00:00:00 2001 From: Adrien Champion Date: Wed, 1 Apr 2026 09:47:43 +0200 Subject: [PATCH 2/6] chore: remove obsolete known projects from readme --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index de1737d..9e4b5bd 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,6 @@ efficient as uses Rust's `HashMap`s, not a custom built structure. For more details see [the documentation][doc]. -# Known projects using `hashconsing` - -- [kinō][kino], a model-checker for transition systems -- [hoice][hoice], a machine-learning-based predicate synthesizer for horn clauses - # License MIT/Apache-2.0 From eea4724d4f2212a6b19cf152a3737832789137aa Mon Sep 17 00:00:00 2001 From: Adrien Champion Date: Wed, 1 Apr 2026 09:54:21 +0200 Subject: [PATCH 3/6] chore: fix warnings --- src/coll.rs | 14 ++++++++------ src/hash_coll.rs | 14 ++++++++------ src/hash_coll/hashers.rs | 12 ++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/coll.rs b/src/coll.rs index 85692ef..2855128 100644 --- a/src/coll.rs +++ b/src/coll.rs @@ -178,7 +178,7 @@ where } /// An iterator visiting all elements. #[inline] - pub fn iter(&self) -> ::std::collections::hash_set::Iter> { + pub fn iter<'a>(&'a self) -> ::std::collections::hash_set::Iter<'a, HConsed> { self.set.iter() } } @@ -322,12 +322,14 @@ where } /// An iterator visiting all elements. #[inline] - pub fn iter(&self) -> ::std::collections::hash_map::Iter, V> { + pub fn iter<'a>(&'a self) -> ::std::collections::hash_map::Iter<'a, HConsed, V> { self.map.iter() } /// An iterator visiting all elements. #[inline] - pub fn iter_mut(&mut self) -> ::std::collections::hash_map::IterMut, V> { + pub fn iter_mut<'a>( + &'a mut self, + ) -> ::std::collections::hash_map::IterMut<'a, HConsed, V> { self.map.iter_mut() } } @@ -437,7 +439,7 @@ mod hash { impl HashU64 { /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(debug)] + #[cfg(debug_assertions)] #[inline(always)] fn test_bytes(bytes: &[u8]) { if bytes.len() != 8 { @@ -451,13 +453,13 @@ mod hash { } /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(not(debug))] + #[cfg(not(debug_assertions))] #[inline(always)] fn test_bytes(_: &[u8]) {} } impl Hasher for HashU64 { fn finish(&self) -> u64 { - unsafe { ::std::mem::transmute(self.buf) } + u64::from_ne_bytes(self.buf) } fn write(&mut self, bytes: &[u8]) { Self::test_bytes(bytes); diff --git a/src/hash_coll.rs b/src/hash_coll.rs index 37d2623..90dfdab 100644 --- a/src/hash_coll.rs +++ b/src/hash_coll.rs @@ -322,7 +322,7 @@ where { /// An iterator visiting all elements. #[inline] - pub fn iter(&self) -> ::std::collections::hash_set::Iter> { + pub fn iter<'a>(&'a self) -> ::std::collections::hash_set::Iter<'a, HConsed> { self.set.iter() } } @@ -520,12 +520,14 @@ where { /// An iterator visiting all elements. #[inline] - pub fn iter(&self) -> ::std::collections::hash_map::Iter, V> { + pub fn iter<'a>(&'a self) -> ::std::collections::hash_map::Iter<'a, HConsed, V> { self.map.iter() } /// An iterator visiting all elements. #[inline] - pub fn iter_mut(&mut self) -> ::std::collections::hash_map::IterMut, V> { + pub fn iter_mut<'a>( + &'a mut self, + ) -> ::std::collections::hash_map::IterMut<'a, HConsed, V> { self.map.iter_mut() } } @@ -643,7 +645,7 @@ mod hash { impl HashU64 { /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(debug)] + #[cfg(debug_assertions)] #[inline(always)] fn test_bytes(bytes: &[u8]) { if bytes.len() != 8 { @@ -657,13 +659,13 @@ mod hash { } /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(not(debug))] + #[cfg(not(debug_assertions))] #[inline(always)] fn test_bytes(_: &[u8]) {} } impl Hasher for HashU64 { fn finish(&self) -> u64 { - let block: u64 = unsafe { ::std::mem::transmute(self.buf) }; + let block: u64 = u64::from_ne_bytes(self.buf); // Multiply by random 64-bit prime to distribute block.wrapping_mul(0xDA5DF7A7BD02F2C7u64) } diff --git a/src/hash_coll/hashers.rs b/src/hash_coll/hashers.rs index c7f5e3d..dc77af3 100644 --- a/src/hash_coll/hashers.rs +++ b/src/hash_coll/hashers.rs @@ -50,7 +50,7 @@ pub mod p_hash { impl Hasher { /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(debug)] + #[cfg(debug_assertions)] #[inline(always)] fn test_bytes(bytes: &[u8]) { if bytes.len() != 8 { @@ -64,13 +64,13 @@ pub mod p_hash { } /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(not(debug))] + #[cfg(not(debug_assertions))] #[inline(always)] fn test_bytes(_: &[u8]) {} } impl StdHasherExt for Hasher { fn finish(&self) -> u64 { - let block: u64 = unsafe { ::std::mem::transmute(self.buf) }; + let block: u64 = u64::from_ne_bytes(self.buf); // Multiply by random 64-bit prime to distribute block.wrapping_mul(0xDA5DF7A7BD02F2C7u64) } @@ -112,7 +112,7 @@ pub mod id_hash { impl Hasher { /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(debug)] + #[cfg(debug_assertions)] #[inline(always)] fn test_bytes(bytes: &[u8]) { if bytes.len() != 8 { @@ -126,13 +126,13 @@ pub mod id_hash { } /// Checks that a slice of bytes has the length of a `usize`. Only active /// in debug. - #[cfg(not(debug))] + #[cfg(not(debug_assertions))] #[inline(always)] fn test_bytes(_: &[u8]) {} } impl StdHasherExt for Hasher { fn finish(&self) -> u64 { - unsafe { ::std::mem::transmute(self.buf) } + u64::from_ne_bytes(self.buf) } fn write(&mut self, bytes: &[u8]) { Self::test_bytes(bytes); From 5378fc494a71e7c6e7f17f9e3396ceb9776afeab Mon Sep 17 00:00:00 2001 From: Adrien Champion Date: Wed, 1 Apr 2026 09:56:14 +0200 Subject: [PATCH 4/6] chore: restore `deny(warnings)` on the whole crate --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4c56359..c46e1a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -213,6 +213,8 @@ //! [lazy static]: https://crates.io/crates/lazy_static //! (lazy_static library on crates.io) +#![deny(warnings)] + use std::{ borrow::Borrow, cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, From bc9f5005abd6660d52dc5d469fb01dd7e7e868fa Mon Sep 17 00:00:00 2001 From: Adrien Champion Date: Wed, 1 Apr 2026 10:03:29 +0200 Subject: [PATCH 5/6] fix: address documentation warning --- src/hash_coll.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hash_coll.rs b/src/hash_coll.rs index 90dfdab..a79d87c 100644 --- a/src/hash_coll.rs +++ b/src/hash_coll.rs @@ -42,11 +42,11 @@ //! be the natural one, *e.g.* `HConSet`. //! //! However, since `Term` is really an alias for `HConsed`, then if we wanted to declare -//! `HConSet` as an alias for `HashSet` we would get `type HConSet = HashSet< HConsed -//! >` (omitting the custom hasher). That is, our sets would have type `HConSet`, which is -//! not very pretty. We could just define an alias though: `type TermSet = HConSet`, but it -//! turns out it's better to wrap the actual set in a `struct` anyway. Mostly to be able to define -//! `new` and `with_capacity` without relying on a trait (users would need to import) to do that. +//! `HConSet` as an alias for `HashSet` we would get `type HConSet = HashSet>` +//! (omitting the custom hasher). That is, our sets would have type `HConSet`, which is not +//! very pretty. We could just define an alias though: `type TermSet = HConSet`, but it turns +//! out it's better to wrap the actual set in a `struct` anyway. Mostly to be able to define `new` +//! and `with_capacity` without relying on a trait (users would need to import) to do that. //! //! So actually `HConsed` types automatically implement the internal `trait HashConsed { type Inner; //! }`. The sole purpose of this trait (currently) is to pass the inner type implicitly thanks to a From f943da400940a5f1cd7d1ab6806e1104f1b88f7c Mon Sep 17 00:00:00 2001 From: Adrien Champion Date: Wed, 1 Apr 2026 10:07:12 +0200 Subject: [PATCH 6/6] chore: address clippy warnings --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c46e1a4..ecb0876 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,7 @@ mod test; /// - `$hash_builder:expr` optional hash builder, an /// implementation of [`std::hash::BuildHasher`] ; /// - `$typ:typ,` type being hashconsed (the underlying type, not the -/// hashconsed one) ; +/// hashconsed one) ; #[macro_export] macro_rules! consign { ( @@ -355,7 +355,7 @@ impl Eq for HConsed {} impl PartialOrd for HConsed { #[inline] fn partial_cmp(&self, other: &Self) -> Option { - self.uid.partial_cmp(&other.uid) + Some(self.cmp(other)) } } impl Ord for HConsed { @@ -447,7 +447,7 @@ impl Eq for WHConsed {} impl PartialOrd for WHConsed { #[inline] fn partial_cmp(&self, other: &Self) -> Option { - self.uid.partial_cmp(&other.uid) + Some(self.cmp(other)) } } impl Ord for WHConsed { @@ -674,7 +674,7 @@ pub trait HashConsign: Sized { /// Reserves capacity for at least `additional` more elements. fn reserve(self, additional: usize); } -impl<'a, T: Hash + Eq + Clone, S: BuildHasher> HashConsign for &'a mut HConsign { +impl HashConsign for &mut HConsign { fn mk_is_new(self, elm: T) -> (HConsed, bool) { // If the element is known and upgradable return it. if let Some(hconsed) = self.get(&elm) { @@ -747,7 +747,7 @@ macro_rules! get { }; } -impl<'a, T: Hash + Eq + Clone> HashConsign for &'a RwLock> { +impl HashConsign for &RwLock> { /// If the element is already in the consign, only read access will be /// requested. fn mk_is_new(self, elm: T) -> (HConsed, bool) {