Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
126 changes: 69 additions & 57 deletions src/linked_hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2108,27 +2108,27 @@ struct Node<K, V> {
impl<K, V> Node<K, V> {
#[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() }
}
}

Expand All @@ -2151,46 +2151,52 @@ impl<T> OptNonNullExt<T> for Option<NonNull<T>> {
#[inline]
unsafe fn ensure_guard_node<K, V>(head: &mut Option<NonNull<Node<K, V>>>) {
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);
}
}

// Attach the `to_attach` node to the existing circular list *before* `node`.
#[inline]
unsafe fn attach_before<K, V>(mut to_attach: NonNull<Node<K, V>>, mut node: NonNull<Node<K, V>>) {
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<K, V>(mut node: NonNull<Node<K, V>>) {
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]
unsafe fn push_free<K, V>(
free_list: &mut Option<NonNull<Node<K, V>>>,
mut node: NonNull<Node<K, V>>,
) {
node.as_mut().links.free.next = *free_list;
unsafe { node.as_mut().links.free.next = *free_list };
*free_list = Some(node);
}

Expand All @@ -2199,7 +2205,7 @@ unsafe fn pop_free<K, V>(
free_list: &mut Option<NonNull<Node<K, V>>>,
) -> Option<NonNull<Node<K, V>>> {
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
Expand All @@ -2208,22 +2214,26 @@ unsafe fn pop_free<K, V>(

#[inline]
unsafe fn allocate_node<K, V>(free_list: &mut Option<NonNull<Node<K, V>>>) -> NonNull<Node<K, V>> {
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(),
},
},
},
})))
})))
}
}
}

Expand All @@ -2234,11 +2244,13 @@ unsafe fn drop_value_nodes<K, V>(mut guard: NonNull<Node<K, V>>) {
// 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
Expand All @@ -2264,11 +2276,11 @@ unsafe fn drop_value_nodes<K, V>(mut guard: NonNull<Node<K, V>>) {

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);
}
Expand All @@ -2279,8 +2291,8 @@ unsafe fn drop_value_nodes<K, V>(mut guard: NonNull<Node<K, V>>) {
#[inline]
unsafe fn drop_free_nodes<K, V>(mut free: Option<NonNull<Node<K, V>>>) {
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;
}
}
Expand All @@ -2290,9 +2302,11 @@ unsafe fn remove_node<K, V>(
free_list: &mut Option<NonNull<Node<K, V>>>,
mut node: NonNull<Node<K, V>>,
) -> (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]
Expand All @@ -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]
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/linked_hash_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, S = DefaultHashBuilder> {
map: LinkedHashMap<T, (), S>,
Expand Down
2 changes: 1 addition & 1 deletion src/lru_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
Loading
Loading