Apply review feedback
This commit is contained in:
@@ -11,6 +11,7 @@ repository = "https://github.com/bytecodealliance/regalloc2"
|
|||||||
log = { version = "0.4.8", default-features = false }
|
log = { version = "0.4.8", default-features = false }
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
|
slice-group-by = "0.3.0"
|
||||||
|
|
||||||
# The below are only needed for fuzzing.
|
# The below are only needed for fuzzing.
|
||||||
# Keep this in sync with libfuzzer_sys's crate version:
|
# Keep this in sync with libfuzzer_sys's crate version:
|
||||||
|
|||||||
@@ -269,6 +269,15 @@ pub struct PRegData {
|
|||||||
pub is_stack: bool,
|
pub is_stack: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MultiFixedRegFixup {
|
||||||
|
pub pos: ProgPoint,
|
||||||
|
pub from_slot: u8,
|
||||||
|
pub to_slot: u8,
|
||||||
|
pub to_preg: PRegIndex,
|
||||||
|
pub vreg: VRegIndex,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Env<'a, F: Function> {
|
pub struct Env<'a, F: Function> {
|
||||||
pub func: &'a F,
|
pub func: &'a F,
|
||||||
@@ -330,9 +339,7 @@ pub struct Env<'a, F: Function> {
|
|||||||
// the register available. When we produce the final edit-list, we
|
// the register available. When we produce the final edit-list, we
|
||||||
// will insert a copy from wherever the VReg's primary allocation
|
// will insert a copy from wherever the VReg's primary allocation
|
||||||
// was to the approprate PReg.
|
// was to the approprate PReg.
|
||||||
//
|
pub multi_fixed_reg_fixups: Vec<MultiFixedRegFixup>,
|
||||||
// (progpoint, from-slot, copy-to-preg, vreg, to-slot)
|
|
||||||
pub multi_fixed_reg_fixups: Vec<(ProgPoint, u8, PRegIndex, VRegIndex, u8)>,
|
|
||||||
|
|
||||||
pub inserted_moves: Vec<InsertedMove>,
|
pub inserted_moves: Vec<InsertedMove>,
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ use super::{
|
|||||||
SpillSetIndex, Use, VRegData, VRegIndex, SLOT_NONE,
|
SpillSetIndex, Use, VRegData, VRegIndex, SLOT_NONE,
|
||||||
};
|
};
|
||||||
use crate::indexset::IndexSet;
|
use crate::indexset::IndexSet;
|
||||||
use crate::util::SliceGroupBy;
|
use crate::ion::data_structures::MultiFixedRegFixup;
|
||||||
use crate::{
|
use crate::{
|
||||||
Allocation, Block, Function, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
|
Allocation, Block, Function, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
|
||||||
OperandPos, PReg, ProgPoint, RegAllocError, VReg,
|
OperandPos, PReg, ProgPoint, RegAllocError, VReg,
|
||||||
};
|
};
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
|
use slice_group_by::GroupByMut;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
@@ -1184,7 +1185,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// Find groups of uses that occur in at the same program point.
|
// Find groups of uses that occur in at the same program point.
|
||||||
for uses in self.ranges[range.index()]
|
for uses in self.ranges[range.index()]
|
||||||
.uses
|
.uses
|
||||||
.group_by_mut_(|a, b| a.pos == b.pos)
|
.linear_group_by_key_mut(|u| u.pos)
|
||||||
{
|
{
|
||||||
if uses.len() < 2 {
|
if uses.len() < 2 {
|
||||||
continue;
|
continue;
|
||||||
@@ -1254,21 +1255,23 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// FixedStack is incompatible if there are any
|
// FixedStack is incompatible if there are any
|
||||||
// Reg/FixedReg constraints. FixedReg is
|
// Reg/FixedReg constraints. FixedReg is
|
||||||
// incompatible if there already is a different
|
// incompatible if there already is a different
|
||||||
// FixedReg constraint.
|
// FixedReg constraint. If either condition is true,
|
||||||
|
// we edit the constraint below; otherwise, we can
|
||||||
|
// skip this edit.
|
||||||
if !(requires_reg && self.pregs[preg.index()].is_stack)
|
if !(requires_reg && self.pregs[preg.index()].is_stack)
|
||||||
&& *first_preg.get_or_insert(preg) == preg
|
&& *first_preg.get_or_insert(preg) == preg
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!(" -> duplicate; switching to constraint Reg");
|
log::trace!(" -> duplicate; switching to constraint Any");
|
||||||
self.multi_fixed_reg_fixups.push((
|
self.multi_fixed_reg_fixups.push(MultiFixedRegFixup {
|
||||||
u.pos,
|
pos: u.pos,
|
||||||
source_slot,
|
from_slot: source_slot,
|
||||||
preg_idx,
|
to_slot: u.slot,
|
||||||
vreg_idx,
|
to_preg: preg_idx,
|
||||||
u.slot,
|
vreg: vreg_idx,
|
||||||
));
|
});
|
||||||
u.operand = Operand::new(
|
u.operand = Operand::new(
|
||||||
u.operand.vreg(),
|
u.operand.vreg(),
|
||||||
OperandConstraint::Any,
|
OperandConstraint::Any,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
//! Bundle merging.
|
//! Bundle merging.
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Env, LiveBundleIndex, LiveRangeIndex, LiveRangeKey, Requirement, SpillSet, SpillSetIndex,
|
Env, LiveBundleIndex, LiveRangeIndex, LiveRangeKey, SpillSet, SpillSetIndex, SpillSlotIndex,
|
||||||
SpillSlotIndex, VRegIndex,
|
VRegIndex,
|
||||||
};
|
};
|
||||||
use crate::{Function, Inst, OperandConstraint, PReg};
|
use crate::{Function, Inst, OperandConstraint, PReg};
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
@@ -99,11 +99,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|| self.bundles[to.index()].cached_stack()
|
|| self.bundles[to.index()].cached_stack()
|
||||||
|| self.bundles[to.index()].cached_fixed()
|
|| self.bundles[to.index()].cached_fixed()
|
||||||
{
|
{
|
||||||
let req = self
|
if self.merge_bundle_requirements(from, to).is_err() {
|
||||||
.compute_requirement(from)
|
|
||||||
.0
|
|
||||||
.merge(self.compute_requirement(to).0);
|
|
||||||
if req == Requirement::Conflict {
|
|
||||||
log::trace!(" -> conflicting requirements; aborting merge");
|
log::trace!(" -> conflicting requirements; aborting merge");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -695,29 +695,27 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle multi-fixed-reg constraints by copying.
|
// Handle multi-fixed-reg constraints by copying.
|
||||||
for (progpoint, from_slot, to_preg, to_vreg, to_slot) in
|
for fixup in std::mem::replace(&mut self.multi_fixed_reg_fixups, vec![]) {
|
||||||
std::mem::replace(&mut self.multi_fixed_reg_fixups, vec![])
|
let from_alloc = self.get_alloc(fixup.pos.inst(), fixup.from_slot as usize);
|
||||||
{
|
let to_alloc = Allocation::reg(self.pregs[fixup.to_preg.index()].reg);
|
||||||
let from_alloc = self.get_alloc(progpoint.inst(), from_slot as usize);
|
|
||||||
let to_alloc = Allocation::reg(self.pregs[to_preg.index()].reg);
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"multi-fixed-move constraint at {:?} from {} to {} for v{}",
|
"multi-fixed-move constraint at {:?} from {} to {} for v{}",
|
||||||
progpoint,
|
fixup.pos,
|
||||||
from_alloc,
|
from_alloc,
|
||||||
to_alloc,
|
to_alloc,
|
||||||
to_vreg.index(),
|
fixup.vreg.index(),
|
||||||
);
|
);
|
||||||
self.insert_move(
|
self.insert_move(
|
||||||
progpoint,
|
fixup.pos,
|
||||||
InsertMovePrio::MultiFixedReg,
|
InsertMovePrio::MultiFixedReg,
|
||||||
from_alloc,
|
from_alloc,
|
||||||
to_alloc,
|
to_alloc,
|
||||||
Some(self.vreg_regs[to_vreg.index()]),
|
Some(self.vreg_regs[fixup.vreg.index()]),
|
||||||
);
|
);
|
||||||
self.set_alloc(
|
self.set_alloc(
|
||||||
progpoint.inst(),
|
fixup.pos.inst(),
|
||||||
to_slot as usize,
|
fixup.to_slot as usize,
|
||||||
Allocation::reg(self.pregs[to_preg.index()].reg),
|
Allocation::reg(self.pregs[fixup.to_preg.index()].reg),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,12 @@ use super::{
|
|||||||
Requirement, SpillWeight, UseList,
|
Requirement, SpillWeight, UseList,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ion::data_structures::{
|
ion::{
|
||||||
CodeRange, BUNDLE_MAX_NORMAL_SPILL_WEIGHT, MINIMAL_BUNDLE_SPILL_WEIGHT,
|
data_structures::{
|
||||||
MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT,
|
CodeRange, BUNDLE_MAX_NORMAL_SPILL_WEIGHT, MINIMAL_BUNDLE_SPILL_WEIGHT,
|
||||||
|
MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT,
|
||||||
|
},
|
||||||
|
requirement::RequirementConflictAt,
|
||||||
},
|
},
|
||||||
Allocation, Function, Inst, InstPosition, OperandConstraint, OperandKind, PReg, ProgPoint,
|
Allocation, Function, Inst, InstPosition, OperandConstraint, OperandKind, PReg, ProgPoint,
|
||||||
RegAllocError,
|
RegAllocError,
|
||||||
@@ -729,7 +732,6 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
reg_hint: PReg,
|
reg_hint: PReg,
|
||||||
) -> Result<(), RegAllocError> {
|
) -> Result<(), RegAllocError> {
|
||||||
let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class;
|
let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class;
|
||||||
let (req, split_point) = self.compute_requirement(bundle);
|
|
||||||
// Grab a hint from either the queue or our spillset, if any.
|
// Grab a hint from either the queue or our spillset, if any.
|
||||||
let mut hint_reg = if reg_hint != PReg::invalid() {
|
let mut hint_reg = if reg_hint != PReg::invalid() {
|
||||||
reg_hint
|
reg_hint
|
||||||
@@ -741,27 +743,30 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
}
|
}
|
||||||
log::trace!("process_bundle: bundle {:?} hint {:?}", bundle, hint_reg,);
|
log::trace!("process_bundle: bundle {:?} hint {:?}", bundle, hint_reg,);
|
||||||
|
|
||||||
if let Requirement::Conflict = req {
|
let req = match self.compute_requirement(bundle) {
|
||||||
// We have to split right away. We'll find a point to
|
Ok(req) => req,
|
||||||
// split that would allow at least the first half of the
|
Err(RequirementConflictAt(split_point)) => {
|
||||||
// split to be conflict-free.
|
// We have to split right away. We'll find a point to
|
||||||
assert!(
|
// split that would allow at least the first half of the
|
||||||
!self.minimal_bundle(bundle),
|
// split to be conflict-free.
|
||||||
"Minimal bundle with conflict!"
|
assert!(
|
||||||
);
|
!self.minimal_bundle(bundle),
|
||||||
self.split_and_requeue_bundle(
|
"Minimal bundle with conflict!"
|
||||||
bundle,
|
);
|
||||||
/* split_at_point = */ split_point,
|
self.split_and_requeue_bundle(
|
||||||
reg_hint,
|
bundle,
|
||||||
);
|
/* split_at_point = */ split_point,
|
||||||
return Ok(());
|
reg_hint,
|
||||||
}
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// If no requirement at all (because no uses), and *if* a
|
// If no requirement at all (because no uses), and *if* a
|
||||||
// spill bundle is already present, then move the LRs over to
|
// spill bundle is already present, then move the LRs over to
|
||||||
// the spill bundle right away.
|
// the spill bundle right away.
|
||||||
match req {
|
match req {
|
||||||
Requirement::Unknown | Requirement::Any => {
|
Requirement::Any => {
|
||||||
if let Some(spill) =
|
if let Some(spill) =
|
||||||
self.get_or_create_spill_bundle(bundle, /* create_if_absent = */ false)
|
self.get_or_create_spill_bundle(bundle, /* create_if_absent = */ false)
|
||||||
{
|
{
|
||||||
@@ -794,14 +799,10 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Requirement::Any | Requirement::Unknown => {
|
Requirement::Any => {
|
||||||
self.spilled_bundles.push(bundle);
|
self.spilled_bundles.push(bundle);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Requirement::Conflict => {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// Scan all pregs, or the one fixed preg, and attempt to allocate.
|
// Scan all pregs, or the one fixed preg, and attempt to allocate.
|
||||||
|
|
||||||
|
|||||||
@@ -15,32 +15,36 @@
|
|||||||
use super::{Env, LiveBundleIndex};
|
use super::{Env, LiveBundleIndex};
|
||||||
use crate::{Function, Operand, OperandConstraint, PReg, ProgPoint};
|
use crate::{Function, Operand, OperandConstraint, PReg, ProgPoint};
|
||||||
|
|
||||||
|
pub struct RequirementConflict;
|
||||||
|
|
||||||
|
pub struct RequirementConflictAt(pub ProgPoint);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Requirement {
|
pub enum Requirement {
|
||||||
Unknown,
|
|
||||||
FixedReg(PReg),
|
FixedReg(PReg),
|
||||||
FixedStack(PReg),
|
FixedStack(PReg),
|
||||||
Register,
|
Register,
|
||||||
Stack,
|
Stack,
|
||||||
Any,
|
Any,
|
||||||
Conflict,
|
|
||||||
}
|
}
|
||||||
impl Requirement {
|
impl Requirement {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn merge(self, other: Requirement) -> Requirement {
|
pub fn merge(self, other: Requirement) -> Result<Requirement, RequirementConflict> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Requirement::Unknown, other) | (other, Requirement::Unknown) => other,
|
(other, Requirement::Any) | (Requirement::Any, other) => Ok(other),
|
||||||
(Requirement::Conflict, _) | (_, Requirement::Conflict) => Requirement::Conflict,
|
(Requirement::Register, Requirement::Register) => Ok(self),
|
||||||
(other, Requirement::Any) | (Requirement::Any, other) => other,
|
(Requirement::Stack, Requirement::Stack) => Ok(self),
|
||||||
(Requirement::Register, Requirement::Register) => self,
|
|
||||||
(Requirement::Stack, Requirement::Stack) => self,
|
|
||||||
(Requirement::Register, Requirement::FixedReg(preg))
|
(Requirement::Register, Requirement::FixedReg(preg))
|
||||||
| (Requirement::FixedReg(preg), Requirement::Register) => Requirement::FixedReg(preg),
|
| (Requirement::FixedReg(preg), Requirement::Register) => {
|
||||||
|
Ok(Requirement::FixedReg(preg))
|
||||||
|
}
|
||||||
(Requirement::Stack, Requirement::FixedStack(preg))
|
(Requirement::Stack, Requirement::FixedStack(preg))
|
||||||
| (Requirement::FixedStack(preg), Requirement::Stack) => Requirement::FixedStack(preg),
|
| (Requirement::FixedStack(preg), Requirement::Stack) => {
|
||||||
(Requirement::FixedReg(a), Requirement::FixedReg(b)) if a == b => self,
|
Ok(Requirement::FixedStack(preg))
|
||||||
(Requirement::FixedStack(a), Requirement::FixedStack(b)) if a == b => self,
|
}
|
||||||
_ => Requirement::Conflict,
|
(Requirement::FixedReg(a), Requirement::FixedReg(b)) if a == b => Ok(self),
|
||||||
|
(Requirement::FixedStack(a), Requirement::FixedStack(b)) if a == b => Ok(self),
|
||||||
|
_ => Err(RequirementConflict),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,12 +62,15 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
}
|
}
|
||||||
OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register,
|
OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register,
|
||||||
OperandConstraint::Stack => Requirement::Stack,
|
OperandConstraint::Stack => Requirement::Stack,
|
||||||
_ => Requirement::Any,
|
OperandConstraint::Any => Requirement::Any,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_requirement(&self, bundle: LiveBundleIndex) -> (Requirement, ProgPoint) {
|
pub fn compute_requirement(
|
||||||
let mut req = Requirement::Unknown;
|
&self,
|
||||||
|
bundle: LiveBundleIndex,
|
||||||
|
) -> Result<Requirement, RequirementConflictAt> {
|
||||||
|
let mut req = Requirement::Any;
|
||||||
log::trace!("compute_requirement: {:?}", bundle);
|
log::trace!("compute_requirement: {:?}", bundle);
|
||||||
let ranges = &self.bundles[bundle.index()].ranges;
|
let ranges = &self.bundles[bundle.index()].ranges;
|
||||||
for entry in ranges {
|
for entry in ranges {
|
||||||
@@ -71,14 +78,28 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
for u in &self.ranges[entry.index.index()].uses {
|
for u in &self.ranges[entry.index.index()].uses {
|
||||||
log::trace!(" -> use {:?}", u);
|
log::trace!(" -> use {:?}", u);
|
||||||
let r = self.requirement_from_operand(u.operand);
|
let r = self.requirement_from_operand(u.operand);
|
||||||
req = req.merge(r);
|
req = req.merge(r).map_err(|_| {
|
||||||
|
log::trace!(" -> conflict");
|
||||||
|
RequirementConflictAt(u.pos)
|
||||||
|
})?;
|
||||||
log::trace!(" -> req {:?}", req);
|
log::trace!(" -> req {:?}", req);
|
||||||
if req == Requirement::Conflict {
|
|
||||||
return (req, u.pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::trace!(" -> final: {:?}", req);
|
log::trace!(" -> final: {:?}", req);
|
||||||
(req, ranges.first().unwrap().range.from)
|
Ok(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_bundle_requirements(
|
||||||
|
&self,
|
||||||
|
a: LiveBundleIndex,
|
||||||
|
b: LiveBundleIndex,
|
||||||
|
) -> Result<Requirement, RequirementConflict> {
|
||||||
|
let req_a = self
|
||||||
|
.compute_requirement(a)
|
||||||
|
.map_err(|_| RequirementConflict)?;
|
||||||
|
let req_b = self
|
||||||
|
.compute_requirement(b)
|
||||||
|
.map_err(|_| RequirementConflict)?;
|
||||||
|
req_a.merge(req_b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ pub(crate) mod ion;
|
|||||||
pub(crate) mod moves;
|
pub(crate) mod moves;
|
||||||
pub(crate) mod postorder;
|
pub(crate) mod postorder;
|
||||||
pub(crate) mod ssa;
|
pub(crate) mod ssa;
|
||||||
pub(crate) mod util;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod index;
|
mod index;
|
||||||
|
|||||||
224
src/util.rs
224
src/util.rs
@@ -1,224 +0,0 @@
|
|||||||
// This file contains a port of the unstable GroupBy slice iterator from the
|
|
||||||
// Rust standard library. The methods have a trailing underscore to avoid
|
|
||||||
// naming conflicts with the standard library.
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::iter::FusedIterator;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
pub trait SliceGroupBy<T> {
|
|
||||||
/// Returns an iterator over the slice producing non-overlapping runs
|
|
||||||
/// of elements using the predicate to separate them.
|
|
||||||
///
|
|
||||||
/// The predicate is called on two elements following themselves,
|
|
||||||
/// it means the predicate is called on `slice[0]` and `slice[1]`
|
|
||||||
/// then on `slice[1]` and `slice[2]` and so on.
|
|
||||||
fn group_by_<F>(&self, pred: F) -> GroupBy<'_, T, F>
|
|
||||||
where
|
|
||||||
F: FnMut(&T, &T) -> bool;
|
|
||||||
|
|
||||||
/// Returns an iterator over the slice producing non-overlapping mutable
|
|
||||||
/// runs of elements using the predicate to separate them.
|
|
||||||
///
|
|
||||||
/// The predicate is called on two elements following themselves,
|
|
||||||
/// it means the predicate is called on `slice[0]` and `slice[1]`
|
|
||||||
/// then on `slice[1]` and `slice[2]` and so on.
|
|
||||||
fn group_by_mut_<F>(&mut self, pred: F) -> GroupByMut<'_, T, F>
|
|
||||||
where
|
|
||||||
F: FnMut(&T, &T) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> SliceGroupBy<T> for [T] {
|
|
||||||
fn group_by_<F>(&self, pred: F) -> GroupBy<'_, T, F>
|
|
||||||
where
|
|
||||||
F: FnMut(&T, &T) -> bool,
|
|
||||||
{
|
|
||||||
GroupBy::new(self, pred)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn group_by_mut_<F>(&mut self, pred: F) -> GroupByMut<'_, T, F>
|
|
||||||
where
|
|
||||||
F: FnMut(&T, &T) -> bool,
|
|
||||||
{
|
|
||||||
GroupByMut::new(self, pred)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An iterator over slice in (non-overlapping) chunks separated by a predicate.
|
|
||||||
pub struct GroupBy<'a, T: 'a, P> {
|
|
||||||
slice: &'a [T],
|
|
||||||
predicate: P,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> GroupBy<'a, T, P> {
|
|
||||||
pub(super) fn new(slice: &'a [T], predicate: P) -> Self {
|
|
||||||
GroupBy { slice, predicate }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> Iterator for GroupBy<'a, T, P>
|
|
||||||
where
|
|
||||||
P: FnMut(&T, &T) -> bool,
|
|
||||||
{
|
|
||||||
type Item = &'a [T];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.slice.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut len = 1;
|
|
||||||
let mut iter = self.slice.windows(2);
|
|
||||||
while let Some([l, r]) = iter.next() {
|
|
||||||
if (self.predicate)(l, r) {
|
|
||||||
len += 1
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (head, tail) = self.slice.split_at(len);
|
|
||||||
self.slice = tail;
|
|
||||||
Some(head)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
if self.slice.is_empty() {
|
|
||||||
(0, Some(0))
|
|
||||||
} else {
|
|
||||||
(1, Some(self.slice.len()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn last(mut self) -> Option<Self::Item> {
|
|
||||||
self.next_back()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> DoubleEndedIterator for GroupBy<'a, T, P>
|
|
||||||
where
|
|
||||||
P: FnMut(&T, &T) -> bool,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.slice.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut len = 1;
|
|
||||||
let mut iter = self.slice.windows(2);
|
|
||||||
while let Some([l, r]) = iter.next_back() {
|
|
||||||
if (self.predicate)(l, r) {
|
|
||||||
len += 1
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (head, tail) = self.slice.split_at(self.slice.len() - len);
|
|
||||||
self.slice = head;
|
|
||||||
Some(tail)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> FusedIterator for GroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool {}
|
|
||||||
|
|
||||||
impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupBy<'a, T, P> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("GroupBy")
|
|
||||||
.field("slice", &self.slice)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An iterator over slice in (non-overlapping) mutable chunks separated
|
|
||||||
/// by a predicate.
|
|
||||||
pub struct GroupByMut<'a, T: 'a, P> {
|
|
||||||
slice: &'a mut [T],
|
|
||||||
predicate: P,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> GroupByMut<'a, T, P> {
|
|
||||||
pub(super) fn new(slice: &'a mut [T], predicate: P) -> Self {
|
|
||||||
GroupByMut { slice, predicate }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> Iterator for GroupByMut<'a, T, P>
|
|
||||||
where
|
|
||||||
P: FnMut(&T, &T) -> bool,
|
|
||||||
{
|
|
||||||
type Item = &'a mut [T];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.slice.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut len = 1;
|
|
||||||
let mut iter = self.slice.windows(2);
|
|
||||||
while let Some([l, r]) = iter.next() {
|
|
||||||
if (self.predicate)(l, r) {
|
|
||||||
len += 1
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let slice = mem::take(&mut self.slice);
|
|
||||||
let (head, tail) = slice.split_at_mut(len);
|
|
||||||
self.slice = tail;
|
|
||||||
Some(head)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
if self.slice.is_empty() {
|
|
||||||
(0, Some(0))
|
|
||||||
} else {
|
|
||||||
(1, Some(self.slice.len()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn last(mut self) -> Option<Self::Item> {
|
|
||||||
self.next_back()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> DoubleEndedIterator for GroupByMut<'a, T, P>
|
|
||||||
where
|
|
||||||
P: FnMut(&T, &T) -> bool,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.slice.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut len = 1;
|
|
||||||
let mut iter = self.slice.windows(2);
|
|
||||||
while let Some([l, r]) = iter.next_back() {
|
|
||||||
if (self.predicate)(l, r) {
|
|
||||||
len += 1
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let slice = mem::take(&mut self.slice);
|
|
||||||
let (head, tail) = slice.split_at_mut(slice.len() - len);
|
|
||||||
self.slice = head;
|
|
||||||
Some(tail)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: 'a, P> FusedIterator for GroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {}
|
|
||||||
|
|
||||||
impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupByMut<'a, T, P> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("GroupByMut")
|
|
||||||
.field("slice", &self.slice)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user