Skip to content

Commit e23a022

Browse files
authored
Merge pull request #6 from cuviper/release-0.1.2
Release 0.1.2
2 parents 6b9915e + 05831a1 commit e23a022

File tree

8 files changed

+201
-15
lines changed

8 files changed

+201
-15
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "ringmap"
33
edition = "2021"
4-
version = "0.1.1"
4+
version = "0.1.2"
55
documentation = "https://docs.rs/ringmap/"
66
repository = "https://github.com/indexmap-rs/ringmap"
77
license = "Apache-2.0 OR MIT"
@@ -28,7 +28,7 @@ default-features = false
2828

2929
[dev-dependencies]
3030
itertools = "0.14"
31-
rand = {version = "0.8", features = ["small_rng"] }
31+
rand = {version = "0.9", features = ["small_rng"] }
3232
quickcheck = { version = "1.0", default-features = false }
3333
fnv = "1.0"
3434
lazy_static = "1.3"

RELEASES.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# Releases
22

3-
## 0.1.1
3+
## 0.1.2 (2025-03-10)
4+
5+
- Added `ringmap_with_default!` and `ringset_with_default!` to be used with
6+
alternative hashers, especially when using the crate without `std`.
7+
- Implemented `PartialEq` between each `Slice` and `[]`/arrays.
8+
9+
## 0.1.1 (2025-01-29)
410

511
- Optimized the branch behavior of the iterators.
612

7-
## 0.1.0
13+
## 0.1.0 (2025-01-21)
814

915
- Initial release, based on `indexmap v2.7.1`.

src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
//! [`with_capacity_and_hasher`][RingMap::with_capacity_and_hasher] instead.
9696
//! A no-std compatible hasher will be needed as well, for example
9797
//! from the crate `twox-hash`.
98-
//! - Macros [`ringmap!`] and [`ringset!`] are unavailable without `std`.
98+
//! - Macros [`ringmap!`] and [`ringset!`] are unavailable without `std`. Use
99+
//! the macros [`ringmap_with_default!`] and [`ringset_with_default!`] instead.
99100
100101
#![cfg_attr(docsrs, feature(doc_cfg))]
101102

src/macros.rs

+74
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,40 @@
1+
/// Create an [`RingMap`][crate::RingMap] from a list of key-value pairs
2+
/// and a `BuildHasherDefault`-wrapped custom hasher.
3+
///
4+
/// ## Example
5+
///
6+
/// ```
7+
/// use ringmap::ringmap_with_default;
8+
/// use fnv::FnvHasher;
9+
///
10+
/// let map = ringmap_with_default!{
11+
/// FnvHasher;
12+
/// "a" => 1,
13+
/// "b" => 2,
14+
/// };
15+
/// assert_eq!(map["a"], 1);
16+
/// assert_eq!(map["b"], 2);
17+
/// assert_eq!(map.get("c"), None);
18+
///
19+
/// // "a" is the first key
20+
/// assert_eq!(map.keys().next(), Some(&"a"));
21+
/// ```
22+
#[macro_export]
23+
macro_rules! ringmap_with_default {
24+
($H:ty; $($key:expr => $value:expr,)+) => { $crate::ringmap_with_default!($H; $($key => $value),+) };
25+
($H:ty; $($key:expr => $value:expr),*) => {{
26+
let builder = ::core::hash::BuildHasherDefault::<$H>::default();
27+
const CAP: usize = <[()]>::len(&[$({ stringify!($key); }),*]);
28+
#[allow(unused_mut)]
29+
// Specify your custom `H` (must implement Default + Hasher) as the hasher:
30+
let mut map = $crate::RingMap::with_capacity_and_hasher(CAP, builder);
31+
$(
32+
map.insert($key, $value);
33+
)*
34+
map
35+
}};
36+
}
37+
138
#[cfg(feature = "std")]
239
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
340
#[macro_export]
@@ -35,6 +72,43 @@ macro_rules! ringmap {
3572
};
3673
}
3774

75+
/// Create an [`RingSet`][crate::RingSet] from a list of values
76+
/// and a `BuildHasherDefault`-wrapped custom hasher.
77+
///
78+
/// ## Example
79+
///
80+
/// ```
81+
/// use ringmap::ringset_with_default;
82+
/// use fnv::FnvHasher;
83+
///
84+
/// let set = ringset_with_default!{
85+
/// FnvHasher;
86+
/// "a",
87+
/// "b",
88+
/// };
89+
/// assert!(set.contains("a"));
90+
/// assert!(set.contains("b"));
91+
/// assert!(!set.contains("c"));
92+
///
93+
/// // "a" is the first value
94+
/// assert_eq!(set.iter().next(), Some(&"a"));
95+
/// ```
96+
#[macro_export]
97+
macro_rules! ringset_with_default {
98+
($H:ty; $($value:expr,)+) => { $crate::ringset_with_default!($H; $($value),+) };
99+
($H:ty; $($value:expr),*) => {{
100+
let builder = ::core::hash::BuildHasherDefault::<$H>::default();
101+
const CAP: usize = <[()]>::len(&[$({ stringify!($value); }),*]);
102+
#[allow(unused_mut)]
103+
// Specify your custom `H` (must implement Default + Hash) as the hasher:
104+
let mut set = $crate::RingSet::with_capacity_and_hasher(CAP, builder);
105+
$(
106+
set.insert($value);
107+
)*
108+
set
109+
}};
110+
}
111+
38112
#[cfg(feature = "std")]
39113
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
40114
#[macro_export]

src/map/iter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -626,8 +626,8 @@ impl<K, V> Default for Keys<'_, K, V> {
626626
/// [values]: RingMap#impl-Index<usize>-for-RingMap<K,+V,+S>
627627
///
628628
/// Since `Keys` is also an iterator, consuming items from the iterator will
629-
/// offset the effective indexes. Similarly, if `Keys` is obtained from
630-
/// [`Slice::keys`][super::Slice::keys], indexes will be interpreted relative to the position of
629+
/// offset the effective indices. Similarly, if `Keys` is obtained from
630+
/// [`Slice::keys`][super::Slice::keys], indices will be interpreted relative to the position of
631631
/// that slice.
632632
///
633633
/// # Examples

src/map/slice.rs

+50-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{Bucket, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut};
2-
use crate::util::try_simplify_range;
2+
use crate::util::{slice_eq, try_simplify_range};
33

44
use alloc::boxed::Box;
55
use alloc::collections::VecDeque;
@@ -329,9 +329,55 @@ impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for Slice<K, V> {
329329
}
330330
}
331331

332-
impl<K: PartialEq, V: PartialEq> PartialEq for Slice<K, V> {
333-
fn eq(&self, other: &Self) -> bool {
334-
self.len() == other.len() && self.iter().eq(other)
332+
impl<K, V, K2, V2> PartialEq<Slice<K2, V2>> for Slice<K, V>
333+
where
334+
K: PartialEq<K2>,
335+
V: PartialEq<V2>,
336+
{
337+
fn eq(&self, other: &Slice<K2, V2>) -> bool {
338+
slice_eq(&self.entries, &other.entries, |b1, b2| {
339+
b1.key == b2.key && b1.value == b2.value
340+
})
341+
}
342+
}
343+
344+
impl<K, V, K2, V2> PartialEq<[(K2, V2)]> for Slice<K, V>
345+
where
346+
K: PartialEq<K2>,
347+
V: PartialEq<V2>,
348+
{
349+
fn eq(&self, other: &[(K2, V2)]) -> bool {
350+
slice_eq(&self.entries, other, |b, t| b.key == t.0 && b.value == t.1)
351+
}
352+
}
353+
354+
impl<K, V, K2, V2> PartialEq<Slice<K2, V2>> for [(K, V)]
355+
where
356+
K: PartialEq<K2>,
357+
V: PartialEq<V2>,
358+
{
359+
fn eq(&self, other: &Slice<K2, V2>) -> bool {
360+
slice_eq(self, &other.entries, |t, b| t.0 == b.key && t.1 == b.value)
361+
}
362+
}
363+
364+
impl<K, V, K2, V2, const N: usize> PartialEq<[(K2, V2); N]> for Slice<K, V>
365+
where
366+
K: PartialEq<K2>,
367+
V: PartialEq<V2>,
368+
{
369+
fn eq(&self, other: &[(K2, V2); N]) -> bool {
370+
<Self as PartialEq<[_]>>::eq(self, other)
371+
}
372+
}
373+
374+
impl<K, V, const N: usize, K2, V2> PartialEq<Slice<K2, V2>> for [(K, V); N]
375+
where
376+
K: PartialEq<K2>,
377+
V: PartialEq<V2>,
378+
{
379+
fn eq(&self, other: &Slice<K2, V2>) -> bool {
380+
<[_] as PartialEq<_>>::eq(self, other)
335381
}
336382
}
337383

src/set/slice.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{Bucket, IntoIter, Iter};
2-
use crate::util::try_simplify_range;
2+
use crate::util::{slice_eq, try_simplify_range};
33

44
use alloc::boxed::Box;
55
use alloc::collections::VecDeque;
@@ -219,9 +219,48 @@ impl<T: fmt::Debug> fmt::Debug for Slice<T> {
219219
}
220220
}
221221

222-
impl<T: PartialEq> PartialEq for Slice<T> {
223-
fn eq(&self, other: &Self) -> bool {
224-
self.len() == other.len() && self.iter().eq(other)
222+
impl<T, U> PartialEq<Slice<U>> for Slice<T>
223+
where
224+
T: PartialEq<U>,
225+
{
226+
fn eq(&self, other: &Slice<U>) -> bool {
227+
slice_eq(&self.entries, &other.entries, |b1, b2| b1.key == b2.key)
228+
}
229+
}
230+
231+
impl<T, U> PartialEq<[U]> for Slice<T>
232+
where
233+
T: PartialEq<U>,
234+
{
235+
fn eq(&self, other: &[U]) -> bool {
236+
slice_eq(&self.entries, other, |b, o| b.key == *o)
237+
}
238+
}
239+
240+
impl<T, U> PartialEq<Slice<U>> for [T]
241+
where
242+
T: PartialEq<U>,
243+
{
244+
fn eq(&self, other: &Slice<U>) -> bool {
245+
slice_eq(self, &other.entries, |o, b| *o == b.key)
246+
}
247+
}
248+
249+
impl<T, U, const N: usize> PartialEq<[U; N]> for Slice<T>
250+
where
251+
T: PartialEq<U>,
252+
{
253+
fn eq(&self, other: &[U; N]) -> bool {
254+
<Self as PartialEq<[U]>>::eq(self, other)
255+
}
256+
}
257+
258+
impl<T, const N: usize, U> PartialEq<Slice<U>> for [T; N]
259+
where
260+
T: PartialEq<U>,
261+
{
262+
fn eq(&self, other: &Slice<U>) -> bool {
263+
<[T] as PartialEq<Slice<U>>>::eq(self, other)
225264
}
226265
}
227266

src/util.rs

+20
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,23 @@ where
5656
}
5757
Some(start..end)
5858
}
59+
60+
// Generic slice equality -- copied from the standard library but adding a custom comparator,
61+
// allowing for our `Bucket` wrapper on either or both sides.
62+
pub(crate) fn slice_eq<T, U>(left: &[T], right: &[U], eq: impl Fn(&T, &U) -> bool) -> bool {
63+
if left.len() != right.len() {
64+
return false;
65+
}
66+
67+
// Implemented as explicit indexing rather
68+
// than zipped iterators for performance reasons.
69+
// See PR https://github.com/rust-lang/rust/pull/116846
70+
for i in 0..left.len() {
71+
// bound checks are optimized away
72+
if !eq(&left[i], &right[i]) {
73+
return false;
74+
}
75+
}
76+
77+
true
78+
}

0 commit comments

Comments
 (0)