diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96aa992..cc601af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.65.0 + toolchain: 1.85.0 - run: cargo check --locked --lib --all-features lint: diff --git a/Cargo.lock b/Cargo.lock index 99651d7..8e18e9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,16 +10,16 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ "foldhash", ] [[package]] name = "hashlink" -version = "0.11.1" +version = "0.12.0" dependencies = [ "hashbrown", "rustc-hash", diff --git a/Cargo.toml b/Cargo.toml index af7be7f..a573a65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,20 @@ [package] name = "hashlink" -version = "0.11.1" -edition = "2021" +version = "0.12.0" +edition = "2024" description = "HashMap-like containers that hold their key-value pairs in a user controllable order" repository = "https://github.com/djc/hashlink" documentation = "https://docs.rs/hashlink" readme = "README.md" keywords = ["data-structures", "no_std"] license = "MIT OR Apache-2.0" -rust-version = "1.65" +rust-version = "1.85" [features] serde_impl = ["serde"] [dependencies] -hashbrown = { version = "0.16", default-features = false, features = ["default-hasher"] } +hashbrown = { version = "0.17", default-features = false, features = ["default-hasher"] } serde = { version = "1.0", default-features = false, optional = true } [dev-dependencies] diff --git a/src/linked_hash_map.rs b/src/linked_hash_map.rs index 978c74c..11cea17 100644 --- a/src/linked_hash_map.rs +++ b/src/linked_hash_map.rs @@ -2108,27 +2108,27 @@ struct Node { impl Node { #[inline] unsafe fn put_entry(&mut self, entry: (K, V)) { - self.entry.as_mut_ptr().write(entry) + unsafe { self.entry.as_mut_ptr().write(entry) } } #[inline] unsafe fn entry_ref(&self) -> &(K, V) { - &*self.entry.as_ptr() + unsafe { &*self.entry.as_ptr() } } #[inline] unsafe fn key_ref(&self) -> &K { - &(*self.entry.as_ptr()).0 + unsafe { &(*self.entry.as_ptr()).0 } } #[inline] unsafe fn entry_mut(&mut self) -> &mut (K, V) { - &mut *self.entry.as_mut_ptr() + unsafe { &mut *self.entry.as_mut_ptr() } } #[inline] unsafe fn take_entry(&mut self) -> (K, V) { - self.entry.as_ptr().read() + unsafe { self.entry.as_ptr().read() } } } @@ -2151,16 +2151,18 @@ impl OptNonNullExt for Option> { #[inline] unsafe fn ensure_guard_node(head: &mut Option>>) { if head.is_none() { - let mut p = NonNull::new_unchecked(Box::into_raw(Box::new(Node { - entry: MaybeUninit::uninit(), - links: Links { - value: ValueLinks { - next: NonNull::dangling(), - prev: NonNull::dangling(), + let mut p = unsafe { + NonNull::new_unchecked(Box::into_raw(Box::new(Node { + entry: MaybeUninit::uninit(), + links: Links { + value: ValueLinks { + next: NonNull::dangling(), + prev: NonNull::dangling(), + }, }, - }, - }))); - p.as_mut().links.value = ValueLinks { next: p, prev: p }; + }))) + }; + unsafe { p.as_mut().links.value = ValueLinks { next: p, prev: p } }; *head = Some(p); } } @@ -2168,21 +2170,25 @@ unsafe fn ensure_guard_node(head: &mut Option>>) { // Attach the `to_attach` node to the existing circular list *before* `node`. #[inline] unsafe fn attach_before(mut to_attach: NonNull>, mut node: NonNull>) { - to_attach.as_mut().links.value = ValueLinks { - prev: node.as_ref().links.value.prev, - next: node, - }; - node.as_mut().links.value.prev = to_attach; - (*to_attach.as_mut().links.value.prev.as_ptr()) - .links - .value - .next = to_attach; + unsafe { + to_attach.as_mut().links.value = ValueLinks { + prev: node.as_ref().links.value.prev, + next: node, + }; + node.as_mut().links.value.prev = to_attach; + (*to_attach.as_mut().links.value.prev.as_ptr()) + .links + .value + .next = to_attach; + } } #[inline] unsafe fn detach_node(mut node: NonNull>) { - node.as_mut().links.value.prev.as_mut().links.value.next = node.as_ref().links.value.next; - node.as_mut().links.value.next.as_mut().links.value.prev = node.as_ref().links.value.prev; + unsafe { + node.as_mut().links.value.prev.as_mut().links.value.next = node.as_ref().links.value.next; + node.as_mut().links.value.next.as_mut().links.value.prev = node.as_ref().links.value.prev; + } } #[inline] @@ -2190,7 +2196,7 @@ unsafe fn push_free( free_list: &mut Option>>, mut node: NonNull>, ) { - node.as_mut().links.free.next = *free_list; + unsafe { node.as_mut().links.free.next = *free_list }; *free_list = Some(node); } @@ -2199,7 +2205,7 @@ unsafe fn pop_free( free_list: &mut Option>>, ) -> Option>> { if let Some(free) = *free_list { - *free_list = free.as_ref().links.free.next; + *free_list = unsafe { free.as_ref().links.free.next }; Some(free) } else { None @@ -2208,22 +2214,26 @@ unsafe fn pop_free( #[inline] unsafe fn allocate_node(free_list: &mut Option>>) -> NonNull> { - if let Some(mut free) = pop_free(free_list) { - free.as_mut().links.value = ValueLinks { - next: NonNull::dangling(), - prev: NonNull::dangling(), - }; + if let Some(mut free) = unsafe { pop_free(free_list) } { + unsafe { + free.as_mut().links.value = ValueLinks { + next: NonNull::dangling(), + prev: NonNull::dangling(), + }; + } free } else { - NonNull::new_unchecked(Box::into_raw(Box::new(Node { - entry: MaybeUninit::uninit(), - links: Links { - value: ValueLinks { - next: NonNull::dangling(), - prev: NonNull::dangling(), + unsafe { + NonNull::new_unchecked(Box::into_raw(Box::new(Node { + entry: MaybeUninit::uninit(), + links: Links { + value: ValueLinks { + next: NonNull::dangling(), + prev: NonNull::dangling(), + }, }, - }, - }))) + }))) + } } } @@ -2234,11 +2244,13 @@ unsafe fn drop_value_nodes(mut guard: NonNull>) { // guard is always left as an empty, consistent list. This matters when an // entry's `Drop` panics: without it, a caught panic (e.g. via `clear`) // could observe a node whose entry was already moved out and drop it again. - let cur = guard.as_ref().links.value.prev; - guard.as_mut().links.value = ValueLinks { - prev: guard, - next: guard, - }; + let cur = unsafe { guard.as_ref().links.value.prev }; + unsafe { + guard.as_mut().links.value = ValueLinks { + prev: guard, + next: guard, + }; + } // `Remainder` owns the not-yet-freed tail of the detached chain. If // dropping an entry panics, its `Drop` frees the remaining nodes during @@ -2264,11 +2276,11 @@ unsafe fn drop_value_nodes(mut guard: NonNull>) { let mut rem = Remainder { cur, guard }; while rem.cur != guard { - let prev = rem.cur.as_ref().links.value.prev; - let entry = rem.cur.as_mut().take_entry(); + let prev = unsafe { rem.cur.as_ref().links.value.prev }; + let entry = unsafe { rem.cur.as_mut().take_entry() }; // Free the node and advance past it before dropping the entry, so that // if the entry's `Drop` panics, `Remainder` resumes from the next node. - let _ = Box::from_raw(rem.cur.as_ptr()); + let _ = unsafe { Box::from_raw(rem.cur.as_ptr()) }; rem.cur = prev; drop(entry); } @@ -2279,8 +2291,8 @@ unsafe fn drop_value_nodes(mut guard: NonNull>) { #[inline] unsafe fn drop_free_nodes(mut free: Option>>) { while let Some(some_free) = free { - let next_free = some_free.as_ref().links.free.next; - let _ = Box::from_raw(some_free.as_ptr()); + let next_free = unsafe { some_free.as_ref().links.free.next }; + let _ = unsafe { Box::from_raw(some_free.as_ptr()) }; free = next_free; } } @@ -2290,9 +2302,11 @@ unsafe fn remove_node( free_list: &mut Option>>, mut node: NonNull>, ) -> (K, V) { - detach_node(node); - push_free(free_list, node); - node.as_mut().take_entry() + unsafe { + detach_node(node); + push_free(free_list, node); + node.as_mut().take_entry() + } } #[inline] @@ -2301,7 +2315,7 @@ where S: BuildHasher, K: Hash, { - hash_key(s, node.as_ref().key_ref()) + hash_key(s, unsafe { node.as_ref().key_ref() }) } #[inline] @@ -2310,9 +2324,7 @@ where S: BuildHasher, Q: Hash + ?Sized, { - let mut hasher = s.build_hasher(); - k.hash(&mut hasher); - hasher.finish() + s.hash_one(k) } // We do not drop the key and value when a value is filtered from the map during the call to diff --git a/src/linked_hash_set.rs b/src/linked_hash_set.rs index bc2122f..db32b44 100644 --- a/src/linked_hash_set.rs +++ b/src/linked_hash_set.rs @@ -6,8 +6,8 @@ use core::{ ops::{BitAnd, BitOr, BitXor, Sub}, }; -use crate::linked_hash_map::{self, LinkedHashMap, TryReserveError}; use crate::DefaultHashBuilder; +use crate::linked_hash_map::{self, LinkedHashMap, TryReserveError}; pub struct LinkedHashSet { map: LinkedHashMap, diff --git a/src/lru_cache.rs b/src/lru_cache.rs index d38a382..2ed2d08 100644 --- a/src/lru_cache.rs +++ b/src/lru_cache.rs @@ -4,8 +4,8 @@ use core::{ hash::{BuildHasher, Hash}, }; -use crate::linked_hash_map::{self, LinkedHashMap}; use crate::DefaultHashBuilder; +use crate::linked_hash_map::{self, LinkedHashMap}; pub use crate::linked_hash_map::{ Drain, Entry, IntoIter, Iter, IterMut, OccupiedEntry, RawEntryBuilder, RawEntryBuilderMut, diff --git a/src/serde.rs b/src/serde.rs index 57c3b16..6b40be2 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -5,9 +5,9 @@ use core::{ }; use serde::{ + Deserialize, Deserializer, Serialize, Serializer, de::{MapAccess, SeqAccess, Visitor}, ser::{SerializeMap, SerializeSeq}, - Deserialize, Deserializer, Serialize, Serializer, }; use crate::{LinkedHashMap, LinkedHashSet}; diff --git a/tests/linked_hash_map.rs b/tests/linked_hash_map.rs index 5dbc535..1f2b401 100644 --- a/tests/linked_hash_map.rs +++ b/tests/linked_hash_map.rs @@ -1,11 +1,11 @@ use std::{ cell::{Cell, RefCell}, hash::{Hash, Hasher}, - panic::{catch_unwind, AssertUnwindSafe}, + panic::{AssertUnwindSafe, catch_unwind}, rc::Rc, }; -use hashlink::{linked_hash_map, LinkedHashMap}; +use hashlink::{LinkedHashMap, linked_hash_map}; #[allow(dead_code)] fn assert_covariance() { @@ -528,24 +528,27 @@ fn test_replace() { map.insert(3, 3); map.insert(4, 4); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(1, 1), (2, 2), (3, 3), (4, 4)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(1, 1), (2, 2), (3, 3), (4, 4)].iter().copied()) + ); map.insert(3, 5); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(1, 1), (2, 2), (4, 4), (3, 5)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(1, 1), (2, 2), (4, 4), (3, 5)].iter().copied()) + ); map.replace(2, 6); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(1, 1), (2, 6), (4, 4), (3, 5)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(1, 1), (2, 6), (4, 4), (3, 5)].iter().copied()) + ); } #[test] @@ -712,10 +715,11 @@ fn test_cursor_mut_insert_before() { // Insert new element in the middle if let linked_hash_map::Entry::Occupied(entry) = map.entry(4) { entry.cursor_mut().insert_before(5, 5); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(3, 3), (5, 5), (4, 4)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(3, 3), (5, 5), (4, 4)].iter().copied()) + ); } // Insert new element at the very end of the list @@ -723,19 +727,21 @@ fn test_cursor_mut_insert_before() { let mut cursor = entry.cursor_mut(); cursor.move_prev(); cursor.insert_before(6, 6); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(3, 3), (5, 5), (4, 4), (6, 6)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(3, 3), (5, 5), (4, 4), (6, 6)].iter().copied()) + ); } // Relocate element and override value if let linked_hash_map::Entry::Occupied(entry) = map.entry(5) { entry.cursor_mut().insert_before(4, 42); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(3, 3), (4, 42), (5, 5), (6, 6)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(3, 3), (4, 42), (5, 5), (6, 6)].iter().copied()) + ); } } @@ -749,10 +755,11 @@ fn test_cursor_mut_insert_after() { // Insert new element in the middle. if let linked_hash_map::Entry::Occupied(entry) = map.entry(3) { entry.cursor_mut().insert_after(5, 5); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(3, 3), (5, 5), (4, 4)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(3, 3), (5, 5), (4, 4)].iter().copied()) + ); } // Insert new element as the first one. @@ -760,10 +767,11 @@ fn test_cursor_mut_insert_after() { let mut cursor = entry.cursor_mut(); cursor.move_next(); cursor.insert_after(6, 6); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(6, 6), (3, 3), (5, 5), (4, 4)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(6, 6), (3, 3), (5, 5), (4, 4)].iter().copied()) + ); } } @@ -779,10 +787,11 @@ fn test_cursor_mut_insert_before_itself() { // handled explicitly. if let linked_hash_map::Entry::Occupied(entry) = map.entry(3) { entry.cursor_mut().insert_before(3, 5); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(2, 2), (3, 5), (4, 4)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(2, 2), (3, 5), (4, 4)].iter().copied()) + ); } } @@ -802,10 +811,11 @@ fn test_cursor_front_mut() { cursor.move_next(); assert!(cursor.current().is_none()); - assert!(map - .iter() - .map(|(k, v)| (*k, *v)) - .eq([(1, 1)].iter().copied())); + assert!( + map.iter() + .map(|(k, v)| (*k, *v)) + .eq([(1, 1)].iter().copied()) + ); map.insert(2, 2); map.insert(3, 3); diff --git a/tests/linked_hash_set.rs b/tests/linked_hash_set.rs index e910ab3..168deae 100644 --- a/tests/linked_hash_set.rs +++ b/tests/linked_hash_set.rs @@ -1,5 +1,5 @@ -use hashlink::linked_hash_set::{self, LinkedHashSet}; use hashlink::DefaultHashBuilder; +use hashlink::linked_hash_set::{self, LinkedHashSet}; #[allow(dead_code)] fn assert_covariance() { diff --git a/tests/serde.rs b/tests/serde.rs index 2cf4a3e..cc2dc6e 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -4,7 +4,7 @@ use std::hash::BuildHasherDefault; use hashlink::{LinkedHashMap, LinkedHashSet}; use rustc_hash::FxHasher; -use serde_test::{assert_tokens, Token}; +use serde_test::{Token, assert_tokens}; #[test] fn map_serde_tokens_empty() {