feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+680
@@ -0,0 +1,680 @@
|
||||
use super::{Bucket, IndexSet, Slice};
|
||||
use crate::inner::{Core, ExtractCore};
|
||||
|
||||
use alloc::vec::{self, Vec};
|
||||
use core::fmt;
|
||||
use core::hash::{BuildHasher, Hash};
|
||||
use core::iter::{Chain, FusedIterator};
|
||||
use core::ops::RangeBounds;
|
||||
use core::slice::Iter as SliceIter;
|
||||
|
||||
impl<'a, T, S> IntoIterator for &'a IndexSet<T, S> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> IntoIterator for IndexSet<T, S> {
|
||||
type Item = T;
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self.into_entries())
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the items of an [`IndexSet`].
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::iter`] method.
|
||||
/// See its documentation for more.
|
||||
pub struct Iter<'a, T> {
|
||||
iter: SliceIter<'a, Bucket<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iter<'a, T> {
|
||||
pub(super) fn new(entries: &'a [Bucket<T>]) -> Self {
|
||||
Self {
|
||||
iter: entries.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a slice of the remaining entries in the iterator.
|
||||
pub fn as_slice(&self) -> &'a Slice<T> {
|
||||
Slice::from_slice(self.iter.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
iterator_methods!(Bucket::key_ref);
|
||||
}
|
||||
|
||||
impl<T> DoubleEndedIterator for Iter<'_, T> {
|
||||
double_ended_iterator_methods!(Bucket::key_ref);
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for Iter<'_, T> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FusedIterator for Iter<'_, T> {}
|
||||
|
||||
impl<T> Clone for Iter<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
Iter {
|
||||
iter: self.iter.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.clone()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Iter<'_, T> {
|
||||
fn default() -> Self {
|
||||
Self { iter: [].iter() }
|
||||
}
|
||||
}
|
||||
|
||||
/// An owning iterator over the items of an [`IndexSet`].
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::into_iter`] method
|
||||
/// (provided by the [`IntoIterator`] trait). See its documentation for more.
|
||||
#[derive(Clone)]
|
||||
pub struct IntoIter<T> {
|
||||
iter: vec::IntoIter<Bucket<T>>,
|
||||
}
|
||||
|
||||
impl<T> IntoIter<T> {
|
||||
pub(super) fn new(entries: Vec<Bucket<T>>) -> Self {
|
||||
Self {
|
||||
iter: entries.into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a slice of the remaining entries in the iterator.
|
||||
pub fn as_slice(&self) -> &Slice<T> {
|
||||
Slice::from_slice(self.iter.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
|
||||
iterator_methods!(Bucket::key);
|
||||
}
|
||||
|
||||
impl<T> DoubleEndedIterator for IntoIter<T> {
|
||||
double_ended_iterator_methods!(Bucket::key);
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for IntoIter<T> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FusedIterator for IntoIter<T> {}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for IntoIter<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let iter = self.iter.as_slice().iter().map(Bucket::key_ref);
|
||||
f.debug_list().entries(iter).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for IntoIter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
iter: Vec::new().into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A draining iterator over the items of an [`IndexSet`].
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::drain`] method.
|
||||
/// See its documentation for more.
|
||||
pub struct Drain<'a, T> {
|
||||
iter: vec::Drain<'a, Bucket<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Drain<'a, T> {
|
||||
pub(super) fn new(iter: vec::Drain<'a, Bucket<T>>) -> Self {
|
||||
Self { iter }
|
||||
}
|
||||
|
||||
/// Returns a slice of the remaining entries in the iterator.
|
||||
pub fn as_slice(&self) -> &Slice<T> {
|
||||
Slice::from_slice(self.iter.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for Drain<'_, T> {
|
||||
type Item = T;
|
||||
|
||||
iterator_methods!(Bucket::key);
|
||||
}
|
||||
|
||||
impl<T> DoubleEndedIterator for Drain<'_, T> {
|
||||
double_ended_iterator_methods!(Bucket::key);
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for Drain<'_, T> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FusedIterator for Drain<'_, T> {}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Drain<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let iter = self.iter.as_slice().iter().map(Bucket::key_ref);
|
||||
f.debug_list().entries(iter).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy iterator producing elements in the difference of [`IndexSet`]s.
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::difference`] method.
|
||||
/// See its documentation for more.
|
||||
pub struct Difference<'a, T, S> {
|
||||
iter: Iter<'a, T>,
|
||||
other: &'a IndexSet<T, S>,
|
||||
}
|
||||
|
||||
impl<'a, T, S> Difference<'a, T, S> {
|
||||
pub(super) fn new<S1>(set: &'a IndexSet<T, S1>, other: &'a IndexSet<T, S>) -> Self {
|
||||
Self {
|
||||
iter: set.iter(),
|
||||
other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, S> Iterator for Difference<'a, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(item) = self.iter.next() {
|
||||
if !self.other.contains(item) {
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, self.iter.size_hint().1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> DoubleEndedIterator for Difference<'_, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
while let Some(item) = self.iter.next_back() {
|
||||
if !self.other.contains(item) {
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> FusedIterator for Difference<'_, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, S> Clone for Difference<'_, T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Difference {
|
||||
iter: self.iter.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> fmt::Debug for Difference<'_, T, S>
|
||||
where
|
||||
T: fmt::Debug + Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.clone()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy iterator producing elements in the intersection of [`IndexSet`]s.
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::intersection`] method.
|
||||
/// See its documentation for more.
|
||||
pub struct Intersection<'a, T, S> {
|
||||
iter: Iter<'a, T>,
|
||||
other: &'a IndexSet<T, S>,
|
||||
}
|
||||
|
||||
impl<'a, T, S> Intersection<'a, T, S> {
|
||||
pub(super) fn new<S1>(set: &'a IndexSet<T, S1>, other: &'a IndexSet<T, S>) -> Self {
|
||||
Self {
|
||||
iter: set.iter(),
|
||||
other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, S> Iterator for Intersection<'a, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(item) = self.iter.next() {
|
||||
if self.other.contains(item) {
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, self.iter.size_hint().1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> DoubleEndedIterator for Intersection<'_, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
while let Some(item) = self.iter.next_back() {
|
||||
if self.other.contains(item) {
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> FusedIterator for Intersection<'_, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, S> Clone for Intersection<'_, T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Intersection {
|
||||
iter: self.iter.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> fmt::Debug for Intersection<'_, T, S>
|
||||
where
|
||||
T: fmt::Debug + Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.clone()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy iterator producing elements in the symmetric difference of [`IndexSet`]s.
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::symmetric_difference`] method.
|
||||
/// See its documentation for more.
|
||||
pub struct SymmetricDifference<'a, T, S1, S2> {
|
||||
iter: Chain<Difference<'a, T, S2>, Difference<'a, T, S1>>,
|
||||
}
|
||||
|
||||
impl<'a, T, S1, S2> SymmetricDifference<'a, T, S1, S2>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S1: BuildHasher,
|
||||
S2: BuildHasher,
|
||||
{
|
||||
pub(super) fn new(set1: &'a IndexSet<T, S1>, set2: &'a IndexSet<T, S2>) -> Self {
|
||||
let diff1 = set1.difference(set2);
|
||||
let diff2 = set2.difference(set1);
|
||||
Self {
|
||||
iter: diff1.chain(diff2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, S1, S2> Iterator for SymmetricDifference<'a, T, S1, S2>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S1: BuildHasher,
|
||||
S2: BuildHasher,
|
||||
{
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn fold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.iter.fold(init, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1, S2> DoubleEndedIterator for SymmetricDifference<'_, T, S1, S2>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S1: BuildHasher,
|
||||
S2: BuildHasher,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back()
|
||||
}
|
||||
|
||||
fn rfold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.iter.rfold(init, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1, S2> FusedIterator for SymmetricDifference<'_, T, S1, S2>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S1: BuildHasher,
|
||||
S2: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, S1, S2> Clone for SymmetricDifference<'_, T, S1, S2> {
|
||||
fn clone(&self) -> Self {
|
||||
SymmetricDifference {
|
||||
iter: self.iter.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1, S2> fmt::Debug for SymmetricDifference<'_, T, S1, S2>
|
||||
where
|
||||
T: fmt::Debug + Eq + Hash,
|
||||
S1: BuildHasher,
|
||||
S2: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.clone()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy iterator producing elements in the union of [`IndexSet`]s.
|
||||
///
|
||||
/// This `struct` is created by the [`IndexSet::union`] method.
|
||||
/// See its documentation for more.
|
||||
pub struct Union<'a, T, S> {
|
||||
iter: Chain<Iter<'a, T>, Difference<'a, T, S>>,
|
||||
}
|
||||
|
||||
impl<'a, T, S> Union<'a, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
pub(super) fn new<S2>(set1: &'a IndexSet<T, S>, set2: &'a IndexSet<T, S2>) -> Self
|
||||
where
|
||||
S2: BuildHasher,
|
||||
{
|
||||
Self {
|
||||
iter: set1.iter().chain(set2.difference(set1)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, S> Iterator for Union<'a, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn fold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.iter.fold(init, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> DoubleEndedIterator for Union<'_, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back()
|
||||
}
|
||||
|
||||
fn rfold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.iter.rfold(init, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> FusedIterator for Union<'_, T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, S> Clone for Union<'_, T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Union {
|
||||
iter: self.iter.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> fmt::Debug for Union<'_, T, S>
|
||||
where
|
||||
T: fmt::Debug + Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.clone()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A splicing iterator for `IndexSet`.
|
||||
///
|
||||
/// This `struct` is created by [`IndexSet::splice()`].
|
||||
/// See its documentation for more.
|
||||
pub struct Splice<'a, I, T, S>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
iter: crate::map::Splice<'a, UnitValue<I>, T, (), S>,
|
||||
}
|
||||
|
||||
impl<'a, I, T, S> Splice<'a, I, T, S>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
#[track_caller]
|
||||
pub(super) fn new<R>(set: &'a mut IndexSet<T, S>, range: R, replace_with: I) -> Self
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
Self {
|
||||
iter: set.map.splice(range, UnitValue(replace_with)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S> Iterator for Splice<'_, I, T, S>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(self.iter.next()?.0)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S> DoubleEndedIterator for Splice<'_, I, T, S>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(self.iter.next_back()?.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S> ExactSizeIterator for Splice<'_, I, T, S>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S> FusedIterator for Splice<'_, I, T, S>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
struct UnitValue<I>(I);
|
||||
|
||||
impl<I: Iterator> Iterator for UnitValue<I> {
|
||||
type Item = (I::Item, ());
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next().map(|x| (x, ()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S> fmt::Debug for Splice<'_, I, T, S>
|
||||
where
|
||||
I: fmt::Debug + Iterator<Item = T>,
|
||||
T: fmt::Debug + Hash + Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.iter, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: fmt::Debug> fmt::Debug for UnitValue<I> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An extracting iterator for `IndexSet`.
|
||||
///
|
||||
/// This `struct` is created by [`IndexSet::extract_if()`].
|
||||
/// See its documentation for more.
|
||||
pub struct ExtractIf<'a, T, F> {
|
||||
inner: ExtractCore<'a, T, ()>,
|
||||
pred: F,
|
||||
}
|
||||
|
||||
impl<T, F> ExtractIf<'_, T, F> {
|
||||
#[track_caller]
|
||||
pub(super) fn new<R>(core: &mut Core<T, ()>, range: R, pred: F) -> ExtractIf<'_, T, F>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
F: FnMut(&T) -> bool,
|
||||
{
|
||||
ExtractIf {
|
||||
inner: core.extract(range),
|
||||
pred,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> Iterator for ExtractIf<'_, T, F>
|
||||
where
|
||||
F: FnMut(&T) -> bool,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner
|
||||
.extract_if(|bucket| (self.pred)(bucket.key_ref()))
|
||||
.map(Bucket::key)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, Some(self.inner.remaining()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> FusedIterator for ExtractIf<'_, T, F> where F: FnMut(&T) -> bool {}
|
||||
|
||||
impl<T, F> fmt::Debug for ExtractIf<'_, T, F>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ExtractIf").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
use core::hash::{BuildHasher, Hash};
|
||||
|
||||
use super::{Equivalent, IndexSet};
|
||||
use crate::map::MutableKeys;
|
||||
|
||||
/// Opt-in mutable access to [`IndexSet`] values.
|
||||
///
|
||||
/// These methods expose `&mut T`, mutable references to the value as it is stored
|
||||
/// in the set.
|
||||
/// You are allowed to modify the values in the set **if the modification
|
||||
/// does not change the value's hash and equality**.
|
||||
///
|
||||
/// If values are modified erroneously, you can no longer look them up.
|
||||
/// This is sound (memory safe) but a logical error hazard (just like
|
||||
/// implementing `PartialEq`, `Eq`, or `Hash` incorrectly would be).
|
||||
///
|
||||
/// `use` this trait to enable its methods for `IndexSet`.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for types outside this crate.
|
||||
#[expect(private_bounds)]
|
||||
pub trait MutableValues: Sealed {
|
||||
type Value;
|
||||
|
||||
/// Return item index and mutable reference to the value
|
||||
///
|
||||
/// Computes in **O(1)** time (average).
|
||||
fn get_full_mut2<Q>(&mut self, value: &Q) -> Option<(usize, &mut Self::Value)>
|
||||
where
|
||||
Q: ?Sized + Hash + Equivalent<Self::Value>;
|
||||
|
||||
/// Return mutable reference to the value at an index.
|
||||
///
|
||||
/// Valid indices are `0 <= index < self.len()`.
|
||||
///
|
||||
/// Computes in **O(1)** time.
|
||||
fn get_index_mut2(&mut self, index: usize) -> Option<&mut Self::Value>;
|
||||
|
||||
/// Scan through each value in the set and keep those where the
|
||||
/// closure `keep` returns `true`.
|
||||
///
|
||||
/// The values are visited in order, and remaining values keep their order.
|
||||
///
|
||||
/// Computes in **O(n)** time (average).
|
||||
fn retain2<F>(&mut self, keep: F)
|
||||
where
|
||||
F: FnMut(&mut Self::Value) -> bool;
|
||||
}
|
||||
|
||||
/// Opt-in mutable access to [`IndexSet`] values.
|
||||
///
|
||||
/// See [`MutableValues`] for more information.
|
||||
impl<T, S> MutableValues for IndexSet<T, S>
|
||||
where
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn get_full_mut2<Q>(&mut self, value: &Q) -> Option<(usize, &mut T)>
|
||||
where
|
||||
Q: ?Sized + Hash + Equivalent<T>,
|
||||
{
|
||||
match self.map.get_full_mut2(value) {
|
||||
Some((index, value, ())) => Some((index, value)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_index_mut2(&mut self, index: usize) -> Option<&mut T> {
|
||||
match self.map.get_index_mut2(index) {
|
||||
Some((value, ())) => Some(value),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn retain2<F>(&mut self, mut keep: F)
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
self.map.retain2(move |value, ()| keep(value));
|
||||
}
|
||||
}
|
||||
|
||||
trait Sealed {}
|
||||
|
||||
impl<T, S> Sealed for IndexSet<T, S> {}
|
||||
Vendored
+417
@@ -0,0 +1,417 @@
|
||||
use super::{Bucket, IndexSet, IntoIter, Iter};
|
||||
use crate::util::{slice_eq, try_simplify_range};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{self, Bound, Index, RangeBounds};
|
||||
|
||||
/// A dynamically-sized slice of values in an [`IndexSet`].
|
||||
///
|
||||
/// This supports indexed operations much like a `[T]` slice,
|
||||
/// but not any hashed operations on the values.
|
||||
///
|
||||
/// Unlike `IndexSet`, `Slice` does consider the order for [`PartialEq`]
|
||||
/// and [`Eq`], and it also implements [`PartialOrd`], [`Ord`], and [`Hash`].
|
||||
#[repr(transparent)]
|
||||
pub struct Slice<T> {
|
||||
pub(crate) entries: [Bucket<T>],
|
||||
}
|
||||
|
||||
// SAFETY: `Slice<T>` is a transparent wrapper around `[Bucket<T>]`,
|
||||
// and reference lifetimes are bound together in function signatures.
|
||||
#[allow(unsafe_code)]
|
||||
impl<T> Slice<T> {
|
||||
pub(super) const fn from_slice(entries: &[Bucket<T>]) -> &Self {
|
||||
unsafe { &*(entries as *const [Bucket<T>] as *const Self) }
|
||||
}
|
||||
|
||||
pub(super) fn from_boxed(entries: Box<[Bucket<T>]>) -> Box<Self> {
|
||||
unsafe { Box::from_raw(Box::into_raw(entries) as *mut Self) }
|
||||
}
|
||||
|
||||
fn into_boxed(self: Box<Self>) -> Box<[Bucket<T>]> {
|
||||
unsafe { Box::from_raw(Box::into_raw(self) as *mut [Bucket<T>]) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Slice<T> {
|
||||
pub(crate) fn into_entries(self: Box<Self>) -> Vec<Bucket<T>> {
|
||||
self.into_boxed().into_vec()
|
||||
}
|
||||
|
||||
/// Returns an empty slice.
|
||||
pub const fn new<'a>() -> &'a Self {
|
||||
Self::from_slice(&[])
|
||||
}
|
||||
|
||||
/// Return the number of elements in the set slice.
|
||||
pub const fn len(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
/// Returns true if the set slice contains no elements.
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.entries.is_empty()
|
||||
}
|
||||
|
||||
/// Get a value by index.
|
||||
///
|
||||
/// Valid indices are `0 <= index < self.len()`.
|
||||
pub fn get_index(&self, index: usize) -> Option<&T> {
|
||||
self.entries.get(index).map(Bucket::key_ref)
|
||||
}
|
||||
|
||||
/// Returns a slice of values in the given range of indices.
|
||||
///
|
||||
/// Valid indices are `0 <= index < self.len()`.
|
||||
pub fn get_range<R: RangeBounds<usize>>(&self, range: R) -> Option<&Self> {
|
||||
let range = try_simplify_range(range, self.entries.len())?;
|
||||
self.entries.get(range).map(Self::from_slice)
|
||||
}
|
||||
|
||||
/// Get the first value.
|
||||
pub fn first(&self) -> Option<&T> {
|
||||
self.entries.first().map(Bucket::key_ref)
|
||||
}
|
||||
|
||||
/// Get the last value.
|
||||
pub fn last(&self) -> Option<&T> {
|
||||
self.entries.last().map(Bucket::key_ref)
|
||||
}
|
||||
|
||||
/// Divides one slice into two at an index.
|
||||
///
|
||||
/// ***Panics*** if `index > len`.
|
||||
/// For a non-panicking alternative see [`split_at_checked`][Self::split_at_checked].
|
||||
#[track_caller]
|
||||
pub fn split_at(&self, index: usize) -> (&Self, &Self) {
|
||||
let (first, second) = self.entries.split_at(index);
|
||||
(Self::from_slice(first), Self::from_slice(second))
|
||||
}
|
||||
|
||||
/// Divides one slice into two at an index.
|
||||
///
|
||||
/// Returns `None` if `index > len`.
|
||||
pub fn split_at_checked(&self, index: usize) -> Option<(&Self, &Self)> {
|
||||
let (first, second) = self.entries.split_at_checked(index)?;
|
||||
Some((Self::from_slice(first), Self::from_slice(second)))
|
||||
}
|
||||
|
||||
/// Returns the first value and the rest of the slice,
|
||||
/// or `None` if it is empty.
|
||||
pub fn split_first(&self) -> Option<(&T, &Self)> {
|
||||
if let [first, rest @ ..] = &self.entries {
|
||||
Some((&first.key, Self::from_slice(rest)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the last value and the rest of the slice,
|
||||
/// or `None` if it is empty.
|
||||
pub fn split_last(&self) -> Option<(&T, &Self)> {
|
||||
if let [rest @ .., last] = &self.entries {
|
||||
Some((&last.key, Self::from_slice(rest)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator over the values of the set slice.
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
Iter::new(&self.entries)
|
||||
}
|
||||
|
||||
/// Search over a sorted set for a value.
|
||||
///
|
||||
/// Returns the position where that value is present, or the position where it can be inserted
|
||||
/// to maintain the sort. See [`slice::binary_search`] for more details.
|
||||
///
|
||||
/// Computes in **O(log(n))** time, which is notably less scalable than looking the value up in
|
||||
/// the set this is a slice from using [`IndexSet::get_index_of`], but this can also position
|
||||
/// missing values.
|
||||
pub fn binary_search(&self, x: &T) -> Result<usize, usize>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
self.binary_search_by(|p| p.cmp(x))
|
||||
}
|
||||
|
||||
/// Search over a sorted set with a comparator function.
|
||||
///
|
||||
/// Returns the position where that value is present, or the position where it can be inserted
|
||||
/// to maintain the sort. See [`slice::binary_search_by`] for more details.
|
||||
///
|
||||
/// Computes in **O(log(n))** time.
|
||||
#[inline]
|
||||
pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
|
||||
where
|
||||
F: FnMut(&'a T) -> Ordering,
|
||||
{
|
||||
self.entries.binary_search_by(move |a| f(&a.key))
|
||||
}
|
||||
|
||||
/// Search over a sorted set with an extraction function.
|
||||
///
|
||||
/// Returns the position where that value is present, or the position where it can be inserted
|
||||
/// to maintain the sort. See [`slice::binary_search_by_key`] for more details.
|
||||
///
|
||||
/// Computes in **O(log(n))** time.
|
||||
#[inline]
|
||||
pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize>
|
||||
where
|
||||
F: FnMut(&'a T) -> B,
|
||||
B: Ord,
|
||||
{
|
||||
self.binary_search_by(|k| f(k).cmp(b))
|
||||
}
|
||||
|
||||
/// Checks if the values of this slice are sorted.
|
||||
#[inline]
|
||||
pub fn is_sorted(&self) -> bool
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
self.entries.is_sorted_by(|a, b| a.key <= b.key)
|
||||
}
|
||||
|
||||
/// Checks if this slice is sorted using the given comparator function.
|
||||
#[inline]
|
||||
pub fn is_sorted_by<'a, F>(&'a self, mut cmp: F) -> bool
|
||||
where
|
||||
F: FnMut(&'a T, &'a T) -> bool,
|
||||
{
|
||||
self.entries.is_sorted_by(move |a, b| cmp(&a.key, &b.key))
|
||||
}
|
||||
|
||||
/// Checks if this slice is sorted using the given sort-key function.
|
||||
#[inline]
|
||||
pub fn is_sorted_by_key<'a, F, K>(&'a self, mut sort_key: F) -> bool
|
||||
where
|
||||
F: FnMut(&'a T) -> K,
|
||||
K: PartialOrd,
|
||||
{
|
||||
self.entries.is_sorted_by_key(move |a| sort_key(&a.key))
|
||||
}
|
||||
|
||||
/// Returns the index of the partition point of a sorted set according to the given predicate
|
||||
/// (the index of the first element of the second partition).
|
||||
///
|
||||
/// See [`slice::partition_point`] for more details.
|
||||
///
|
||||
/// Computes in **O(log(n))** time.
|
||||
#[must_use]
|
||||
pub fn partition_point<P>(&self, mut pred: P) -> usize
|
||||
where
|
||||
P: FnMut(&T) -> bool,
|
||||
{
|
||||
self.entries.partition_point(move |a| pred(&a.key))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a Slice<T> {
|
||||
type IntoIter = Iter<'a, T>;
|
||||
type Item = &'a T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for Box<Slice<T>> {
|
||||
type IntoIter = IntoIter<T>;
|
||||
type Item = T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self.into_entries())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for &'_ Slice<T> {
|
||||
fn default() -> Self {
|
||||
Slice::from_slice(&[])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Box<Slice<T>> {
|
||||
fn default() -> Self {
|
||||
Slice::from_boxed(Box::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for Box<Slice<T>> {
|
||||
fn clone(&self) -> Self {
|
||||
Slice::from_boxed(self.entries.to_vec().into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<&Slice<T>> for Box<Slice<T>> {
|
||||
fn from(slice: &Slice<T>) -> Self {
|
||||
Slice::from_boxed(Box::from(&slice.entries))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Slice<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<Slice<U>> for Slice<T>
|
||||
where
|
||||
T: PartialEq<U>,
|
||||
{
|
||||
fn eq(&self, other: &Slice<U>) -> bool {
|
||||
slice_eq(&self.entries, &other.entries, |b1, b2| b1.key == b2.key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<[U]> for Slice<T>
|
||||
where
|
||||
T: PartialEq<U>,
|
||||
{
|
||||
fn eq(&self, other: &[U]) -> bool {
|
||||
slice_eq(&self.entries, other, |b, o| b.key == *o)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<Slice<U>> for [T]
|
||||
where
|
||||
T: PartialEq<U>,
|
||||
{
|
||||
fn eq(&self, other: &Slice<U>) -> bool {
|
||||
slice_eq(self, &other.entries, |o, b| *o == b.key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, const N: usize> PartialEq<[U; N]> for Slice<T>
|
||||
where
|
||||
T: PartialEq<U>,
|
||||
{
|
||||
fn eq(&self, other: &[U; N]) -> bool {
|
||||
<Self as PartialEq<[U]>>::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize, U> PartialEq<Slice<U>> for [T; N]
|
||||
where
|
||||
T: PartialEq<U>,
|
||||
{
|
||||
fn eq(&self, other: &Slice<U>) -> bool {
|
||||
<[T] as PartialEq<Slice<U>>>::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq> Eq for Slice<T> {}
|
||||
|
||||
impl<T: PartialOrd> PartialOrd for Slice<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.iter().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord> Ord for Slice<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.iter().cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for Slice<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.len().hash(state);
|
||||
for value in self {
|
||||
value.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for Slice<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.entries[index].key
|
||||
}
|
||||
}
|
||||
|
||||
// We can't have `impl<I: RangeBounds<usize>> Index<I>` because that conflicts with `Index<usize>`.
|
||||
// Instead, we repeat the implementations for all the core range types.
|
||||
macro_rules! impl_index {
|
||||
($($range:ty),*) => {$(
|
||||
impl<T, S> Index<$range> for IndexSet<T, S> {
|
||||
type Output = Slice<T>;
|
||||
|
||||
fn index(&self, range: $range) -> &Self::Output {
|
||||
Slice::from_slice(&self.as_entries()[range])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<$range> for Slice<T> {
|
||||
type Output = Self;
|
||||
|
||||
fn index(&self, range: $range) -> &Self::Output {
|
||||
Slice::from_slice(&self.entries[range])
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
impl_index!(
|
||||
ops::Range<usize>,
|
||||
ops::RangeFrom<usize>,
|
||||
ops::RangeFull,
|
||||
ops::RangeInclusive<usize>,
|
||||
ops::RangeTo<usize>,
|
||||
ops::RangeToInclusive<usize>,
|
||||
(Bound<usize>, Bound<usize>)
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn slice_index() {
|
||||
fn check(vec_slice: &[i32], set_slice: &Slice<i32>, sub_slice: &Slice<i32>) {
|
||||
assert_eq!(set_slice as *const _, sub_slice as *const _);
|
||||
itertools::assert_equal(vec_slice, set_slice);
|
||||
}
|
||||
|
||||
let vec: Vec<i32> = (0..10).map(|i| i * i).collect();
|
||||
let set: IndexSet<i32> = vec.iter().cloned().collect();
|
||||
let slice = set.as_slice();
|
||||
|
||||
// RangeFull
|
||||
check(&vec[..], &set[..], &slice[..]);
|
||||
|
||||
for i in 0usize..10 {
|
||||
// Index
|
||||
assert_eq!(vec[i], set[i]);
|
||||
assert_eq!(vec[i], slice[i]);
|
||||
|
||||
// RangeFrom
|
||||
check(&vec[i..], &set[i..], &slice[i..]);
|
||||
|
||||
// RangeTo
|
||||
check(&vec[..i], &set[..i], &slice[..i]);
|
||||
|
||||
// RangeToInclusive
|
||||
check(&vec[..=i], &set[..=i], &slice[..=i]);
|
||||
|
||||
// (Bound<usize>, Bound<usize>)
|
||||
let bounds = (Bound::Excluded(i), Bound::Unbounded);
|
||||
check(&vec[i + 1..], &set[bounds], &slice[bounds]);
|
||||
|
||||
for j in i..=10 {
|
||||
// Range
|
||||
check(&vec[i..j], &set[i..j], &slice[i..j]);
|
||||
}
|
||||
|
||||
for j in i..10 {
|
||||
// RangeInclusive
|
||||
check(&vec[i..=j], &set[i..=j], &slice[i..=j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+1060
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user